mcp-use 0.1.20 โ 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-2HFIPY7C.js +429 -0
- package/dist/chunk-4DEFXVWT.js +680 -0
- package/dist/chunk-JXLQRAW2.js +532 -0
- package/dist/chunk-SHUYVCID.js +6 -0
- package/dist/chunk-YUSC6R6V.js +299 -0
- package/dist/index.cjs +5762 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3767 -22
- package/dist/langfuse-YA2S23SM.js +13 -0
- package/dist/src/agents/remote.d.ts.map +1 -1
- package/dist/src/agents/utils/ai_sdk.d.ts.map +1 -1
- package/dist/src/auth/browser-provider.d.ts +52 -0
- package/dist/src/auth/browser-provider.d.ts.map +1 -0
- package/dist/src/auth/callback.d.ts +6 -0
- package/dist/src/auth/callback.d.ts.map +1 -0
- package/dist/src/auth/index.d.ts +7 -0
- package/dist/src/auth/index.d.ts.map +1 -0
- package/dist/src/auth/types.d.ts +18 -0
- package/dist/src/auth/types.d.ts.map +1 -0
- package/dist/src/browser.cjs +323 -0
- package/dist/src/browser.d.ts +8 -0
- package/dist/src/browser.d.ts.map +1 -0
- package/dist/src/browser.js +9 -0
- package/dist/src/client/base.d.ts +32 -0
- package/dist/src/client/base.d.ts.map +1 -0
- package/dist/src/client.d.ts +19 -16
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/logging.d.ts +1 -1
- package/dist/src/logging.d.ts.map +1 -1
- package/dist/src/oauth-helper.d.ts +125 -0
- package/dist/src/oauth-helper.d.ts.map +1 -0
- package/dist/src/react/index.cjs +986 -0
- package/dist/src/react/index.d.ts +9 -0
- package/dist/src/react/index.d.ts.map +1 -0
- package/dist/src/react/index.js +11 -0
- package/dist/src/react/types.d.ts +139 -0
- package/dist/src/react/types.d.ts.map +1 -0
- package/dist/src/react/useMcp.d.ts +3 -0
- package/dist/src/react/useMcp.d.ts.map +1 -0
- package/dist/src/server/index.cjs +566 -0
- package/dist/src/server/index.d.ts +3 -0
- package/dist/src/server/index.d.ts.map +1 -0
- package/dist/src/server/index.js +9 -0
- package/dist/src/server/logging.d.ts +16 -0
- package/dist/src/server/logging.d.ts.map +1 -0
- package/dist/src/server/mcp-server.d.ts +282 -0
- package/dist/src/server/mcp-server.d.ts.map +1 -0
- package/dist/src/server/types.d.ts +47 -0
- package/dist/src/server/types.d.ts.map +1 -0
- package/dist/src/utils/assert.d.ts +8 -0
- package/dist/src/utils/assert.d.ts.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +72 -40
- package/dist/examples/add_server_tool.d.ts +0 -8
- package/dist/examples/add_server_tool.d.ts.map +0 -1
- package/dist/examples/add_server_tool.js +0 -79
- package/dist/examples/ai_sdk_example.d.ts +0 -23
- package/dist/examples/ai_sdk_example.d.ts.map +0 -1
- package/dist/examples/ai_sdk_example.js +0 -213
- package/dist/examples/airbnb_use.d.ts +0 -10
- package/dist/examples/airbnb_use.d.ts.map +0 -1
- package/dist/examples/airbnb_use.js +0 -43
- package/dist/examples/blender_use.d.ts +0 -15
- package/dist/examples/blender_use.d.ts.map +0 -1
- package/dist/examples/blender_use.js +0 -39
- package/dist/examples/browser_use.d.ts +0 -10
- package/dist/examples/browser_use.d.ts.map +0 -1
- package/dist/examples/browser_use.js +0 -46
- package/dist/examples/chat_example.d.ts +0 -10
- package/dist/examples/chat_example.d.ts.map +0 -1
- package/dist/examples/chat_example.js +0 -86
- package/dist/examples/filesystem_use.d.ts +0 -11
- package/dist/examples/filesystem_use.d.ts.map +0 -1
- package/dist/examples/filesystem_use.js +0 -43
- package/dist/examples/http_example.d.ts +0 -18
- package/dist/examples/http_example.d.ts.map +0 -1
- package/dist/examples/http_example.js +0 -37
- package/dist/examples/mcp_everything.d.ts +0 -6
- package/dist/examples/mcp_everything.d.ts.map +0 -1
- package/dist/examples/mcp_everything.js +0 -25
- package/dist/examples/multi_server_example.d.ts +0 -10
- package/dist/examples/multi_server_example.d.ts.map +0 -1
- package/dist/examples/multi_server_example.js +0 -51
- package/dist/examples/observability.d.ts +0 -6
- package/dist/examples/observability.d.ts.map +0 -1
- package/dist/examples/observability.js +0 -50
- package/dist/examples/stream_example.d.ts +0 -12
- package/dist/examples/stream_example.d.ts.map +0 -1
- package/dist/examples/stream_example.js +0 -198
- package/dist/examples/structured_output.d.ts +0 -9
- package/dist/examples/structured_output.d.ts.map +0 -1
- package/dist/examples/structured_output.js +0 -95
- package/dist/src/adapters/base.js +0 -124
- package/dist/src/adapters/index.js +0 -2
- package/dist/src/adapters/langchain_adapter.js +0 -49
- package/dist/src/agents/base.js +0 -9
- package/dist/src/agents/index.js +0 -3
- package/dist/src/agents/mcp_agent.js +0 -1002
- package/dist/src/agents/prompts/system_prompt_builder.js +0 -40
- package/dist/src/agents/prompts/templates.js +0 -39
- package/dist/src/agents/remote.js +0 -264
- package/dist/src/agents/utils/ai_sdk.js +0 -62
- package/dist/src/agents/utils/index.js +0 -1
- package/dist/src/client.js +0 -133
- package/dist/src/config.js +0 -34
- package/dist/src/connectors/base.js +0 -143
- package/dist/src/connectors/http.js +0 -150
- package/dist/src/connectors/index.js +0 -4
- package/dist/src/connectors/stdio.js +0 -68
- package/dist/src/connectors/websocket.js +0 -157
- package/dist/src/logging.js +0 -217
- package/dist/src/managers/index.js +0 -2
- package/dist/src/managers/server_manager.js +0 -106
- package/dist/src/managers/tools/acquire_active_mcp_server.js +0 -17
- package/dist/src/managers/tools/add_server_from_config.js +0 -40
- package/dist/src/managers/tools/base.js +0 -17
- package/dist/src/managers/tools/connect_mcp_server.js +0 -46
- package/dist/src/managers/tools/index.js +0 -5
- package/dist/src/managers/tools/list_mcp_servers.js +0 -33
- package/dist/src/managers/tools/release_mcp_server_connection.js +0 -19
- package/dist/src/observability/index.js +0 -12
- package/dist/src/observability/langfuse.js +0 -211
- package/dist/src/observability/manager.js +0 -199
- package/dist/src/observability/types.js +0 -4
- package/dist/src/session.js +0 -23
- package/dist/src/task_managers/base.js +0 -127
- package/dist/src/task_managers/index.js +0 -5
- package/dist/src/task_managers/sse.js +0 -43
- package/dist/src/task_managers/stdio.js +0 -51
- package/dist/src/task_managers/streamable_http.js +0 -50
- package/dist/src/task_managers/websocket.js +0 -67
- package/dist/src/telemetry/events.js +0 -44
- package/dist/src/telemetry/index.js +0 -8
- package/dist/src/telemetry/telemetry.js +0 -324
- package/dist/src/telemetry/utils.js +0 -39
- package/dist/tests/ai_sdk_compatibility.test.js +0 -214
- package/dist/tests/stream_events.test.js +0 -307
- package/dist/tests/stream_events_simple.test.js +0 -179
- package/dist/vitest.config.js +0 -21
@@ -1,1002 +0,0 @@
|
|
1
|
-
import { CallbackManager } from '@langchain/core/callbacks/manager';
|
2
|
-
import { AIMessage, HumanMessage, SystemMessage, ToolMessage, } from '@langchain/core/messages';
|
3
|
-
import { OutputParserException } from '@langchain/core/output_parsers';
|
4
|
-
import { ChatPromptTemplate, MessagesPlaceholder, } from '@langchain/core/prompts';
|
5
|
-
import { AgentExecutor, createToolCallingAgent } from 'langchain/agents';
|
6
|
-
import { zodToJsonSchema } from 'zod-to-json-schema';
|
7
|
-
import { LangChainAdapter } from '../adapters/langchain_adapter.js';
|
8
|
-
import { logger } from '../logging.js';
|
9
|
-
import { ServerManager } from '../managers/server_manager.js';
|
10
|
-
import { ObservabilityManager } from '../observability/index.js';
|
11
|
-
import { extractModelInfo, Telemetry } from '../telemetry/index.js';
|
12
|
-
import { createSystemMessage } from './prompts/system_prompt_builder.js';
|
13
|
-
import { DEFAULT_SYSTEM_PROMPT_TEMPLATE, SERVER_MANAGER_SYSTEM_PROMPT_TEMPLATE } from './prompts/templates.js';
|
14
|
-
import { RemoteAgent } from './remote.js';
|
15
|
-
export class MCPAgent {
|
16
|
-
llm;
|
17
|
-
client;
|
18
|
-
connectors;
|
19
|
-
maxSteps;
|
20
|
-
autoInitialize;
|
21
|
-
memoryEnabled;
|
22
|
-
disallowedTools;
|
23
|
-
additionalTools;
|
24
|
-
useServerManager;
|
25
|
-
verbose;
|
26
|
-
observe;
|
27
|
-
systemPrompt;
|
28
|
-
systemPromptTemplateOverride;
|
29
|
-
additionalInstructions;
|
30
|
-
_initialized = false;
|
31
|
-
conversationHistory = [];
|
32
|
-
_agentExecutor = null;
|
33
|
-
sessions = {};
|
34
|
-
systemMessage = null;
|
35
|
-
_tools = [];
|
36
|
-
adapter;
|
37
|
-
serverManager = null;
|
38
|
-
telemetry;
|
39
|
-
modelProvider;
|
40
|
-
modelName;
|
41
|
-
// Observability support
|
42
|
-
observabilityManager;
|
43
|
-
callbacks = [];
|
44
|
-
metadata = {};
|
45
|
-
tags = [];
|
46
|
-
// Remote agent support
|
47
|
-
isRemote = false;
|
48
|
-
remoteAgent = null;
|
49
|
-
constructor(options) {
|
50
|
-
// Handle remote execution
|
51
|
-
if (options.agentId) {
|
52
|
-
this.isRemote = true;
|
53
|
-
this.remoteAgent = new RemoteAgent({
|
54
|
-
agentId: options.agentId,
|
55
|
-
apiKey: options.apiKey,
|
56
|
-
baseUrl: options.baseUrl,
|
57
|
-
});
|
58
|
-
// Set default values for remote agent
|
59
|
-
this.maxSteps = options.maxSteps ?? 5;
|
60
|
-
this.memoryEnabled = options.memoryEnabled ?? true;
|
61
|
-
this.autoInitialize = options.autoInitialize ?? false;
|
62
|
-
this.verbose = options.verbose ?? false;
|
63
|
-
this.observe = options.observe ?? true;
|
64
|
-
this.connectors = [];
|
65
|
-
this.disallowedTools = [];
|
66
|
-
this.additionalTools = [];
|
67
|
-
this.useServerManager = false;
|
68
|
-
this.adapter = new LangChainAdapter();
|
69
|
-
this.telemetry = Telemetry.getInstance();
|
70
|
-
this.modelProvider = 'remote';
|
71
|
-
this.modelName = 'remote-agent';
|
72
|
-
this.observabilityManager = new ObservabilityManager({
|
73
|
-
customCallbacks: options.callbacks,
|
74
|
-
agentId: options.agentId,
|
75
|
-
});
|
76
|
-
this.callbacks = [];
|
77
|
-
return;
|
78
|
-
}
|
79
|
-
// Validate requirements for local execution
|
80
|
-
if (!options.llm) {
|
81
|
-
throw new Error('llm is required for local execution. For remote execution, provide agentId instead.');
|
82
|
-
}
|
83
|
-
this.llm = options.llm;
|
84
|
-
this.client = options.client;
|
85
|
-
this.connectors = options.connectors ?? [];
|
86
|
-
this.maxSteps = options.maxSteps ?? 5;
|
87
|
-
this.autoInitialize = options.autoInitialize ?? false;
|
88
|
-
this.memoryEnabled = options.memoryEnabled ?? true;
|
89
|
-
this.systemPrompt = options.systemPrompt ?? null;
|
90
|
-
this.systemPromptTemplateOverride = options.systemPromptTemplate ?? null;
|
91
|
-
this.additionalInstructions = options.additionalInstructions ?? null;
|
92
|
-
this.disallowedTools = options.disallowedTools ?? [];
|
93
|
-
this.additionalTools = options.additionalTools ?? [];
|
94
|
-
this.useServerManager = options.useServerManager ?? false;
|
95
|
-
this.verbose = options.verbose ?? false;
|
96
|
-
this.observe = options.observe ?? true;
|
97
|
-
if (!this.client && this.connectors.length === 0) {
|
98
|
-
throw new Error('Either \'client\' or at least one \'connector\' must be provided.');
|
99
|
-
}
|
100
|
-
if (this.useServerManager) {
|
101
|
-
if (!this.client) {
|
102
|
-
throw new Error('\'client\' must be provided when \'useServerManager\' is true.');
|
103
|
-
}
|
104
|
-
this.adapter = options.adapter ?? new LangChainAdapter(this.disallowedTools);
|
105
|
-
this.serverManager = options.serverManagerFactory?.(this.client) ?? new ServerManager(this.client, this.adapter);
|
106
|
-
}
|
107
|
-
// Let consumers swap allowed tools dynamically
|
108
|
-
else {
|
109
|
-
this.adapter = options.adapter ?? new LangChainAdapter(this.disallowedTools);
|
110
|
-
}
|
111
|
-
// Initialize telemetry
|
112
|
-
this.telemetry = Telemetry.getInstance();
|
113
|
-
// Track model info for telemetry
|
114
|
-
if (this.llm) {
|
115
|
-
const [provider, name] = extractModelInfo(this.llm);
|
116
|
-
this.modelProvider = provider;
|
117
|
-
this.modelName = name;
|
118
|
-
}
|
119
|
-
else {
|
120
|
-
this.modelProvider = 'unknown';
|
121
|
-
this.modelName = 'unknown';
|
122
|
-
}
|
123
|
-
// Set up observability callbacks using the ObservabilityManager
|
124
|
-
this.observabilityManager = new ObservabilityManager({
|
125
|
-
customCallbacks: options.callbacks,
|
126
|
-
verbose: this.verbose,
|
127
|
-
observe: this.observe,
|
128
|
-
agentId: options.agentId,
|
129
|
-
metadataProvider: () => this.getMetadata(),
|
130
|
-
tagsProvider: () => this.getTags(),
|
131
|
-
});
|
132
|
-
// Make getters configurable for test mocking
|
133
|
-
Object.defineProperty(this, 'agentExecutor', {
|
134
|
-
get: () => this._agentExecutor,
|
135
|
-
configurable: true,
|
136
|
-
});
|
137
|
-
Object.defineProperty(this, 'tools', {
|
138
|
-
get: () => this._tools,
|
139
|
-
configurable: true,
|
140
|
-
});
|
141
|
-
Object.defineProperty(this, 'initialized', {
|
142
|
-
get: () => this._initialized,
|
143
|
-
configurable: true,
|
144
|
-
});
|
145
|
-
}
|
146
|
-
async initialize() {
|
147
|
-
// Skip initialization for remote agents
|
148
|
-
if (this.isRemote) {
|
149
|
-
this._initialized = true;
|
150
|
-
return;
|
151
|
-
}
|
152
|
-
logger.info('๐ Initializing MCP agent and connecting to services...');
|
153
|
-
// Initialize observability callbacks
|
154
|
-
this.callbacks = await this.observabilityManager.getCallbacks();
|
155
|
-
const handlerNames = await this.observabilityManager.getHandlerNames();
|
156
|
-
if (handlerNames.length > 0) {
|
157
|
-
logger.info(`๐ Observability enabled with: ${handlerNames.join(', ')}`);
|
158
|
-
}
|
159
|
-
// If using server manager, initialize it
|
160
|
-
if (this.useServerManager && this.serverManager) {
|
161
|
-
await this.serverManager.initialize();
|
162
|
-
// Get server management tools
|
163
|
-
const managementTools = this.serverManager.tools;
|
164
|
-
this._tools = managementTools;
|
165
|
-
this._tools.push(...this.additionalTools);
|
166
|
-
logger.info(`๐ง Server manager mode active with ${managementTools.length} management tools`);
|
167
|
-
// Create the system message based on available tools
|
168
|
-
await this.createSystemMessageFromTools(this._tools);
|
169
|
-
}
|
170
|
-
else {
|
171
|
-
// Standard initialization - if using client, get or create sessions
|
172
|
-
if (this.client) {
|
173
|
-
// First try to get existing sessions
|
174
|
-
this.sessions = this.client.getAllActiveSessions();
|
175
|
-
logger.info(`๐ Found ${Object.keys(this.sessions).length} existing sessions`);
|
176
|
-
// If no active sessions exist, create new ones
|
177
|
-
if (Object.keys(this.sessions).length === 0) {
|
178
|
-
logger.info('๐ No active sessions found, creating new ones...');
|
179
|
-
this.sessions = await this.client.createAllSessions();
|
180
|
-
logger.info(`โ
Created ${Object.keys(this.sessions).length} new sessions`);
|
181
|
-
}
|
182
|
-
// Create LangChain tools directly from the client using the adapter
|
183
|
-
this._tools = await LangChainAdapter.createTools(this.client);
|
184
|
-
this._tools.push(...this.additionalTools);
|
185
|
-
logger.info(`๐ ๏ธ Created ${this._tools.length} LangChain tools from client`);
|
186
|
-
}
|
187
|
-
else {
|
188
|
-
// Using direct connector - only establish connection
|
189
|
-
logger.info(`๐ Connecting to ${this.connectors.length} direct connectors...`);
|
190
|
-
for (const connector of this.connectors) {
|
191
|
-
if (!connector.isClientConnected) {
|
192
|
-
await connector.connect();
|
193
|
-
}
|
194
|
-
}
|
195
|
-
// Create LangChain tools using the adapter with connectors
|
196
|
-
this._tools = await this.adapter.createToolsFromConnectors(this.connectors);
|
197
|
-
this._tools.push(...this.additionalTools);
|
198
|
-
logger.info(`๐ ๏ธ Created ${this._tools.length} LangChain tools from connectors`);
|
199
|
-
}
|
200
|
-
// Get all tools for system message generation
|
201
|
-
logger.info(`๐งฐ Found ${this._tools.length} tools across all connectors`);
|
202
|
-
// Create the system message based on available tools
|
203
|
-
await this.createSystemMessageFromTools(this._tools);
|
204
|
-
}
|
205
|
-
// Create the agent executor and mark initialized
|
206
|
-
this._agentExecutor = this.createAgent();
|
207
|
-
this._initialized = true;
|
208
|
-
// Add MCP server information to observability metadata
|
209
|
-
const mcpServerInfo = this.getMCPServerInfo();
|
210
|
-
if (Object.keys(mcpServerInfo).length > 0) {
|
211
|
-
this.setMetadata(mcpServerInfo);
|
212
|
-
logger.debug(`MCP server info added to metadata: ${JSON.stringify(mcpServerInfo)}`);
|
213
|
-
}
|
214
|
-
logger.info('โจ Agent initialization complete');
|
215
|
-
}
|
216
|
-
async createSystemMessageFromTools(tools) {
|
217
|
-
const systemPromptTemplate = this.systemPromptTemplateOverride
|
218
|
-
?? DEFAULT_SYSTEM_PROMPT_TEMPLATE;
|
219
|
-
this.systemMessage = createSystemMessage(tools, systemPromptTemplate, SERVER_MANAGER_SYSTEM_PROMPT_TEMPLATE, this.useServerManager, this.disallowedTools, this.systemPrompt ?? undefined, this.additionalInstructions ?? undefined);
|
220
|
-
if (this.memoryEnabled) {
|
221
|
-
this.conversationHistory = [
|
222
|
-
this.systemMessage,
|
223
|
-
...this.conversationHistory.filter(m => !(m instanceof SystemMessage)),
|
224
|
-
];
|
225
|
-
}
|
226
|
-
}
|
227
|
-
createAgent() {
|
228
|
-
if (!this.llm) {
|
229
|
-
throw new Error('LLM is required to create agent');
|
230
|
-
}
|
231
|
-
const systemContent = this.systemMessage?.content ?? 'You are a helpful assistant.';
|
232
|
-
const prompt = ChatPromptTemplate.fromMessages([
|
233
|
-
['system', systemContent],
|
234
|
-
new MessagesPlaceholder('chat_history'),
|
235
|
-
['human', '{input}'],
|
236
|
-
new MessagesPlaceholder('agent_scratchpad'),
|
237
|
-
]);
|
238
|
-
const agent = createToolCallingAgent({
|
239
|
-
llm: this.llm,
|
240
|
-
tools: this._tools,
|
241
|
-
prompt,
|
242
|
-
});
|
243
|
-
return new AgentExecutor({
|
244
|
-
agent,
|
245
|
-
tools: this._tools,
|
246
|
-
maxIterations: this.maxSteps,
|
247
|
-
verbose: this.verbose,
|
248
|
-
returnIntermediateSteps: true,
|
249
|
-
callbacks: this.callbacks,
|
250
|
-
});
|
251
|
-
}
|
252
|
-
getConversationHistory() {
|
253
|
-
return [...this.conversationHistory];
|
254
|
-
}
|
255
|
-
clearConversationHistory() {
|
256
|
-
this.conversationHistory = this.memoryEnabled && this.systemMessage ? [this.systemMessage] : [];
|
257
|
-
}
|
258
|
-
addToHistory(message) {
|
259
|
-
if (this.memoryEnabled)
|
260
|
-
this.conversationHistory.push(message);
|
261
|
-
}
|
262
|
-
getSystemMessage() {
|
263
|
-
return this.systemMessage;
|
264
|
-
}
|
265
|
-
setSystemMessage(message) {
|
266
|
-
this.systemMessage = new SystemMessage(message);
|
267
|
-
if (this.memoryEnabled) {
|
268
|
-
this.conversationHistory = this.conversationHistory.filter(m => !(m instanceof SystemMessage));
|
269
|
-
this.conversationHistory.unshift(this.systemMessage);
|
270
|
-
}
|
271
|
-
if (this._initialized && this._tools.length) {
|
272
|
-
this._agentExecutor = this.createAgent();
|
273
|
-
logger.debug('Agent recreated with new system message');
|
274
|
-
}
|
275
|
-
}
|
276
|
-
setDisallowedTools(disallowedTools) {
|
277
|
-
this.disallowedTools = disallowedTools;
|
278
|
-
this.adapter = new LangChainAdapter(this.disallowedTools);
|
279
|
-
if (this._initialized) {
|
280
|
-
logger.debug('Agent already initialized. Changes will take effect on next initialization.');
|
281
|
-
}
|
282
|
-
}
|
283
|
-
getDisallowedTools() {
|
284
|
-
return this.disallowedTools;
|
285
|
-
}
|
286
|
-
/**
|
287
|
-
* Set metadata for observability traces
|
288
|
-
* @param newMetadata - Key-value pairs to add to metadata. Keys should be strings, values should be serializable.
|
289
|
-
*/
|
290
|
-
setMetadata(newMetadata) {
|
291
|
-
// Validate and sanitize metadata
|
292
|
-
const sanitizedMetadata = this.sanitizeMetadata(newMetadata);
|
293
|
-
// Merge with existing metadata instead of replacing it
|
294
|
-
this.metadata = { ...this.metadata, ...sanitizedMetadata };
|
295
|
-
logger.debug(`Metadata set: ${JSON.stringify(this.metadata)}`);
|
296
|
-
}
|
297
|
-
/**
|
298
|
-
* Get current metadata
|
299
|
-
* @returns A copy of the current metadata object
|
300
|
-
*/
|
301
|
-
getMetadata() {
|
302
|
-
return { ...this.metadata };
|
303
|
-
}
|
304
|
-
/**
|
305
|
-
* Set tags for observability traces
|
306
|
-
* @param newTags - Array of tag strings to add. Duplicates will be automatically removed.
|
307
|
-
*/
|
308
|
-
setTags(newTags) {
|
309
|
-
// Validate and sanitize tags
|
310
|
-
const sanitizedTags = this.sanitizeTags(newTags);
|
311
|
-
this.tags = [...new Set([...this.tags, ...sanitizedTags])]; // Remove duplicates
|
312
|
-
logger.debug(`Tags set: ${JSON.stringify(this.tags)}`);
|
313
|
-
}
|
314
|
-
/**
|
315
|
-
* Get current tags
|
316
|
-
* @returns A copy of the current tags array
|
317
|
-
*/
|
318
|
-
getTags() {
|
319
|
-
return [...this.tags];
|
320
|
-
}
|
321
|
-
/**
|
322
|
-
* Sanitize metadata to ensure compatibility with observability platforms
|
323
|
-
* @param metadata - Raw metadata object
|
324
|
-
* @returns Sanitized metadata object
|
325
|
-
*/
|
326
|
-
sanitizeMetadata(metadata) {
|
327
|
-
const sanitized = {};
|
328
|
-
for (const [key, value] of Object.entries(metadata)) {
|
329
|
-
// Validate key
|
330
|
-
if (typeof key !== 'string' || key.length === 0) {
|
331
|
-
logger.warn(`Invalid metadata key: ${key}. Skipping.`);
|
332
|
-
continue;
|
333
|
-
}
|
334
|
-
// Sanitize key (remove special characters that might cause issues)
|
335
|
-
const sanitizedKey = key.replace(/[^\w-]/g, '_');
|
336
|
-
// Validate and sanitize value
|
337
|
-
if (value === null || value === undefined) {
|
338
|
-
sanitized[sanitizedKey] = value;
|
339
|
-
}
|
340
|
-
else if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
341
|
-
sanitized[sanitizedKey] = value;
|
342
|
-
}
|
343
|
-
else if (Array.isArray(value)) {
|
344
|
-
// Only allow arrays of primitives
|
345
|
-
const sanitizedArray = value.filter(item => typeof item === 'string' || typeof item === 'number' || typeof item === 'boolean');
|
346
|
-
if (sanitizedArray.length > 0) {
|
347
|
-
sanitized[sanitizedKey] = sanitizedArray;
|
348
|
-
}
|
349
|
-
}
|
350
|
-
else if (typeof value === 'object') {
|
351
|
-
// Try to serialize objects, but limit depth to prevent circular references
|
352
|
-
try {
|
353
|
-
const serialized = JSON.stringify(value);
|
354
|
-
if (serialized.length > 1000) {
|
355
|
-
logger.warn(`Metadata value for key '${sanitizedKey}' is too large. Truncating.`);
|
356
|
-
sanitized[sanitizedKey] = `${serialized.substring(0, 1000)}...`;
|
357
|
-
}
|
358
|
-
else {
|
359
|
-
sanitized[sanitizedKey] = value;
|
360
|
-
}
|
361
|
-
}
|
362
|
-
catch (error) {
|
363
|
-
logger.warn(`Failed to serialize metadata value for key '${sanitizedKey}': ${error}. Skipping.`);
|
364
|
-
}
|
365
|
-
}
|
366
|
-
else {
|
367
|
-
logger.warn(`Unsupported metadata value type for key '${sanitizedKey}': ${typeof value}. Skipping.`);
|
368
|
-
}
|
369
|
-
}
|
370
|
-
return sanitized;
|
371
|
-
}
|
372
|
-
/**
|
373
|
-
* Sanitize tags to ensure compatibility with observability platforms
|
374
|
-
* @param tags - Array of tag strings
|
375
|
-
* @returns Array of sanitized tag strings
|
376
|
-
*/
|
377
|
-
sanitizeTags(tags) {
|
378
|
-
return tags
|
379
|
-
.filter(tag => typeof tag === 'string' && tag.length > 0)
|
380
|
-
.map(tag => tag.replace(/[^\w:-]/g, '_'))
|
381
|
-
.filter(tag => tag.length <= 50); // Limit tag length
|
382
|
-
}
|
383
|
-
/**
|
384
|
-
* Get MCP server information for observability metadata
|
385
|
-
*/
|
386
|
-
getMCPServerInfo() {
|
387
|
-
const serverInfo = {};
|
388
|
-
try {
|
389
|
-
if (this.client) {
|
390
|
-
const serverNames = this.client.getServerNames();
|
391
|
-
serverInfo.mcp_servers_count = serverNames.length;
|
392
|
-
serverInfo.mcp_server_names = serverNames;
|
393
|
-
// Get server types and configurations
|
394
|
-
const serverConfigs = {};
|
395
|
-
for (const serverName of serverNames) {
|
396
|
-
try {
|
397
|
-
const config = this.client.getServerConfig(serverName);
|
398
|
-
if (config) {
|
399
|
-
// Determine server type based on configuration
|
400
|
-
let serverType = 'unknown';
|
401
|
-
if (config.command) {
|
402
|
-
serverType = 'command';
|
403
|
-
}
|
404
|
-
else if (config.url) {
|
405
|
-
serverType = 'http';
|
406
|
-
}
|
407
|
-
else if (config.ws_url) {
|
408
|
-
serverType = 'websocket';
|
409
|
-
}
|
410
|
-
serverConfigs[serverName] = {
|
411
|
-
type: serverType,
|
412
|
-
// Include safe configuration details (avoid sensitive data)
|
413
|
-
has_args: !!config.args,
|
414
|
-
has_env: !!config.env,
|
415
|
-
has_headers: !!config.headers,
|
416
|
-
url: config.url || null,
|
417
|
-
command: config.command || null,
|
418
|
-
};
|
419
|
-
}
|
420
|
-
}
|
421
|
-
catch (error) {
|
422
|
-
logger.warn(`Failed to get config for server '${serverName}': ${error}`);
|
423
|
-
serverConfigs[serverName] = { type: 'error', error: 'config_unavailable' };
|
424
|
-
}
|
425
|
-
}
|
426
|
-
serverInfo.mcp_server_configs = serverConfigs;
|
427
|
-
}
|
428
|
-
else if (this.connectors && this.connectors.length > 0) {
|
429
|
-
// Handle direct connectors
|
430
|
-
serverInfo.mcp_servers_count = this.connectors.length;
|
431
|
-
serverInfo.mcp_server_names = this.connectors.map(c => c.publicIdentifier);
|
432
|
-
serverInfo.mcp_server_types = this.connectors.map(c => c.constructor.name);
|
433
|
-
}
|
434
|
-
}
|
435
|
-
catch (error) {
|
436
|
-
logger.warn(`Failed to collect MCP server info: ${error}`);
|
437
|
-
serverInfo.error = 'collection_failed';
|
438
|
-
}
|
439
|
-
return serverInfo;
|
440
|
-
}
|
441
|
-
async _consumeAndReturn(generator) {
|
442
|
-
// Manually iterate through the generator to consume the steps.
|
443
|
-
// The for-await-of loop is not used because it discards the generator's
|
444
|
-
// final return value. We need to capture that value when `done` is true.
|
445
|
-
while (true) {
|
446
|
-
const { done, value } = await generator.next();
|
447
|
-
if (done) {
|
448
|
-
return value;
|
449
|
-
}
|
450
|
-
}
|
451
|
-
}
|
452
|
-
async run(query, maxSteps, manageConnector, externalHistory, outputSchema) {
|
453
|
-
// Delegate to remote agent if in remote mode
|
454
|
-
if (this.isRemote && this.remoteAgent) {
|
455
|
-
return this.remoteAgent.run(query, maxSteps, manageConnector, externalHistory, outputSchema);
|
456
|
-
}
|
457
|
-
const generator = this.stream(query, maxSteps, manageConnector, externalHistory, outputSchema);
|
458
|
-
return this._consumeAndReturn(generator);
|
459
|
-
}
|
460
|
-
/**
|
461
|
-
* Runs the agent and yields intermediate steps as an async generator.
|
462
|
-
* If outputSchema is provided, returns structured output of type T.
|
463
|
-
*/
|
464
|
-
async *stream(query, maxSteps, manageConnector = true, externalHistory, outputSchema) {
|
465
|
-
// Delegate to remote agent if in remote mode
|
466
|
-
if (this.isRemote && this.remoteAgent) {
|
467
|
-
const result = await this.remoteAgent.run(query, maxSteps, manageConnector, externalHistory, outputSchema);
|
468
|
-
return result;
|
469
|
-
}
|
470
|
-
let result = '';
|
471
|
-
let initializedHere = false;
|
472
|
-
const startTime = Date.now();
|
473
|
-
const toolsUsedNames = [];
|
474
|
-
let stepsTaken = 0;
|
475
|
-
let success = false;
|
476
|
-
// Schema-aware setup for structured output
|
477
|
-
let structuredLlm = null;
|
478
|
-
let schemaDescription = '';
|
479
|
-
if (outputSchema) {
|
480
|
-
query = this._enhanceQueryWithSchema(query, outputSchema);
|
481
|
-
logger.debug(`๐ Structured output requested, schema: ${JSON.stringify(zodToJsonSchema(outputSchema), null, 2)}`);
|
482
|
-
// Check if withStructuredOutput method exists
|
483
|
-
if (this.llm && 'withStructuredOutput' in this.llm && typeof this.llm.withStructuredOutput === 'function') {
|
484
|
-
structuredLlm = this.llm.withStructuredOutput(outputSchema);
|
485
|
-
}
|
486
|
-
else if (this.llm) {
|
487
|
-
// Fallback: use the same LLM but we'll handle structure in our helper method
|
488
|
-
structuredLlm = this.llm;
|
489
|
-
}
|
490
|
-
else {
|
491
|
-
throw new Error('LLM is required for structured output');
|
492
|
-
}
|
493
|
-
schemaDescription = JSON.stringify(zodToJsonSchema(outputSchema), null, 2);
|
494
|
-
}
|
495
|
-
try {
|
496
|
-
if (manageConnector && !this._initialized) {
|
497
|
-
await this.initialize();
|
498
|
-
initializedHere = true;
|
499
|
-
}
|
500
|
-
else if (!this._initialized && this.autoInitialize) {
|
501
|
-
await this.initialize();
|
502
|
-
initializedHere = true;
|
503
|
-
}
|
504
|
-
if (!this._agentExecutor) {
|
505
|
-
throw new Error('MCP agent failed to initialize');
|
506
|
-
}
|
507
|
-
const steps = maxSteps ?? this.maxSteps;
|
508
|
-
this._agentExecutor.maxIterations = steps;
|
509
|
-
const display_query = query.length > 50 ? `${query.slice(0, 50).replace(/\n/g, ' ')}...` : query.replace(/\n/g, ' ');
|
510
|
-
logger.info(`๐ฌ Received query: '${display_query}'`);
|
511
|
-
// โโโ Record user message
|
512
|
-
if (this.memoryEnabled) {
|
513
|
-
this.addToHistory(new HumanMessage(query));
|
514
|
-
}
|
515
|
-
const historyToUse = externalHistory ?? this.conversationHistory;
|
516
|
-
const langchainHistory = [];
|
517
|
-
for (const msg of historyToUse) {
|
518
|
-
if (msg instanceof HumanMessage || msg instanceof AIMessage) {
|
519
|
-
langchainHistory.push(msg);
|
520
|
-
}
|
521
|
-
}
|
522
|
-
const intermediateSteps = [];
|
523
|
-
const inputs = { input: query, chat_history: langchainHistory };
|
524
|
-
let nameToToolMap = Object.fromEntries(this._tools.map(t => [t.name, t]));
|
525
|
-
logger.info(`๐ Starting agent execution with max_steps=${steps}`);
|
526
|
-
// Create a run manager with our callbacks if we have any - ONCE for the entire execution
|
527
|
-
let runManager;
|
528
|
-
if (this.callbacks?.length > 0) {
|
529
|
-
// Create an async callback manager with our callbacks
|
530
|
-
const callbackManager = new CallbackManager(undefined, {
|
531
|
-
handlers: this.callbacks,
|
532
|
-
inheritableHandlers: this.callbacks,
|
533
|
-
});
|
534
|
-
// Create a run manager for this chain execution
|
535
|
-
runManager = await callbackManager.handleChainStart({
|
536
|
-
name: 'MCPAgent (mcp-use)',
|
537
|
-
id: ['MCPAgent (mcp-use)'],
|
538
|
-
lc: 1,
|
539
|
-
type: 'not_implemented',
|
540
|
-
}, inputs);
|
541
|
-
}
|
542
|
-
for (let stepNum = 0; stepNum < steps; stepNum++) {
|
543
|
-
stepsTaken = stepNum + 1;
|
544
|
-
if (this.useServerManager && this.serverManager) {
|
545
|
-
const currentTools = this.serverManager.tools;
|
546
|
-
const currentToolNames = new Set(currentTools.map(t => t.name));
|
547
|
-
const existingToolNames = new Set(this._tools.map(t => t.name));
|
548
|
-
const changed = currentTools.length !== this._tools.length
|
549
|
-
|| [...currentToolNames].some(n => !existingToolNames.has(n));
|
550
|
-
if (changed) {
|
551
|
-
logger.info(`๐ Tools changed before step ${stepNum + 1}, updating agent. New tools: ${[...currentToolNames].join(', ')}`);
|
552
|
-
this._tools = currentTools;
|
553
|
-
this._tools.push(...this.additionalTools);
|
554
|
-
await this.createSystemMessageFromTools(this._tools);
|
555
|
-
this._agentExecutor = this.createAgent();
|
556
|
-
this._agentExecutor.maxIterations = steps;
|
557
|
-
nameToToolMap = Object.fromEntries(this._tools.map(t => [t.name, t]));
|
558
|
-
}
|
559
|
-
}
|
560
|
-
logger.info(`๐ฃ Step ${stepNum + 1}/${steps}`);
|
561
|
-
try {
|
562
|
-
logger.debug('Starting agent step execution');
|
563
|
-
const nextStepOutput = await this._agentExecutor._takeNextStep(nameToToolMap, inputs, intermediateSteps, runManager);
|
564
|
-
// Agent finish handling (AgentFinish contains returnValues property)
|
565
|
-
if ('returnValues' in nextStepOutput) {
|
566
|
-
logger.info(`โ
Agent finished at step ${stepNum + 1}`);
|
567
|
-
result = nextStepOutput.returnValues?.output ?? 'No output generated';
|
568
|
-
runManager?.handleChainEnd({ output: result });
|
569
|
-
// If structured output is requested, attempt to create it
|
570
|
-
if (outputSchema && structuredLlm) {
|
571
|
-
try {
|
572
|
-
logger.info('๐ง Attempting structured output...');
|
573
|
-
const structuredResult = await this._attemptStructuredOutput(result, structuredLlm, outputSchema, schemaDescription);
|
574
|
-
logger.debug(`๐ Structured result: ${JSON.stringify(structuredResult)}`);
|
575
|
-
// Add the final response to conversation history if memory is enabled
|
576
|
-
if (this.memoryEnabled) {
|
577
|
-
this.addToHistory(new AIMessage(`Structured result: ${JSON.stringify(structuredResult)}`));
|
578
|
-
}
|
579
|
-
logger.info('โ
Structured output successful');
|
580
|
-
success = true;
|
581
|
-
return structuredResult;
|
582
|
-
}
|
583
|
-
catch (e) {
|
584
|
-
logger.warn(`โ ๏ธ Structured output failed: ${e}`);
|
585
|
-
// Continue execution to gather missing information
|
586
|
-
const failedStructuredOutputPrompt = `
|
587
|
-
The current result cannot be formatted into the required structure.
|
588
|
-
Error: ${String(e)}
|
589
|
-
|
590
|
-
Current information: ${result}
|
591
|
-
|
592
|
-
If information is missing, please continue working to gather the missing information needed for:
|
593
|
-
${schemaDescription}
|
594
|
-
|
595
|
-
If the information is complete, please return the result in the required structure.
|
596
|
-
`;
|
597
|
-
// Add this as feedback and continue the loop
|
598
|
-
inputs.input = failedStructuredOutputPrompt;
|
599
|
-
if (this.memoryEnabled) {
|
600
|
-
this.addToHistory(new HumanMessage(failedStructuredOutputPrompt));
|
601
|
-
}
|
602
|
-
logger.info('๐ Continuing execution to gather missing information...');
|
603
|
-
continue;
|
604
|
-
}
|
605
|
-
}
|
606
|
-
else {
|
607
|
-
// Regular execution without structured output
|
608
|
-
break;
|
609
|
-
}
|
610
|
-
}
|
611
|
-
const stepArray = nextStepOutput;
|
612
|
-
intermediateSteps.push(...stepArray);
|
613
|
-
for (const step of stepArray) {
|
614
|
-
yield step;
|
615
|
-
const { action, observation } = step;
|
616
|
-
const toolName = action.tool;
|
617
|
-
toolsUsedNames.push(toolName);
|
618
|
-
let toolInputStr = typeof action.toolInput === 'string'
|
619
|
-
? action.toolInput
|
620
|
-
: JSON.stringify(action.toolInput, null, 2);
|
621
|
-
if (toolInputStr.length > 100)
|
622
|
-
toolInputStr = `${toolInputStr.slice(0, 97)}...`;
|
623
|
-
logger.info(`๐ง Tool call: ${toolName} with input: ${toolInputStr}`);
|
624
|
-
let outputStr = String(observation);
|
625
|
-
if (outputStr.length > 100)
|
626
|
-
outputStr = `${outputStr.slice(0, 97)}...`;
|
627
|
-
outputStr = outputStr.replace(/\n/g, ' ');
|
628
|
-
logger.info(`๐ Tool result: ${outputStr}`);
|
629
|
-
}
|
630
|
-
// Detect direct return
|
631
|
-
if (stepArray.length) {
|
632
|
-
const lastStep = stepArray[stepArray.length - 1];
|
633
|
-
const toolReturn = await this._agentExecutor._getToolReturn(lastStep);
|
634
|
-
if (toolReturn) {
|
635
|
-
logger.info(`๐ Tool returned directly at step ${stepNum + 1}`);
|
636
|
-
result = toolReturn.returnValues?.output ?? 'No output generated';
|
637
|
-
break;
|
638
|
-
}
|
639
|
-
}
|
640
|
-
}
|
641
|
-
catch (e) {
|
642
|
-
if (e instanceof OutputParserException) {
|
643
|
-
logger.error(`โ Output parsing error during step ${stepNum + 1}: ${e}`);
|
644
|
-
result = `Agent stopped due to a parsing error: ${e}`;
|
645
|
-
runManager?.handleChainError(result);
|
646
|
-
break;
|
647
|
-
}
|
648
|
-
logger.error(`โ Error during agent execution step ${stepNum + 1}: ${e}`);
|
649
|
-
console.error(e);
|
650
|
-
result = `Agent stopped due to an error: ${e}`;
|
651
|
-
runManager?.handleChainError(result);
|
652
|
-
break;
|
653
|
-
}
|
654
|
-
}
|
655
|
-
// โโโ Postโloop handling
|
656
|
-
if (!result) {
|
657
|
-
logger.warn(`โ ๏ธ Agent stopped after reaching max iterations (${steps})`);
|
658
|
-
result = `Agent stopped after reaching the maximum number of steps (${steps}).`;
|
659
|
-
runManager?.handleChainEnd({ output: result });
|
660
|
-
}
|
661
|
-
logger.info('๐ Agent execution complete');
|
662
|
-
success = true;
|
663
|
-
// Return regular result
|
664
|
-
return result;
|
665
|
-
}
|
666
|
-
catch (e) {
|
667
|
-
logger.error(`โ Error running query: ${e}`);
|
668
|
-
if (initializedHere && manageConnector) {
|
669
|
-
logger.info('๐งน Cleaning up resources after initialization error in run');
|
670
|
-
await this.close();
|
671
|
-
}
|
672
|
-
throw e;
|
673
|
-
}
|
674
|
-
finally {
|
675
|
-
// Track comprehensive execution data
|
676
|
-
const executionTimeMs = Date.now() - startTime;
|
677
|
-
let serverCount = 0;
|
678
|
-
if (this.client) {
|
679
|
-
serverCount = Object.keys(this.client.getAllActiveSessions()).length;
|
680
|
-
}
|
681
|
-
else if (this.connectors) {
|
682
|
-
serverCount = this.connectors.length;
|
683
|
-
}
|
684
|
-
const conversationHistoryLength = this.memoryEnabled ? this.conversationHistory.length : 0;
|
685
|
-
await this.telemetry.trackAgentExecution({
|
686
|
-
executionMethod: 'stream',
|
687
|
-
query,
|
688
|
-
success,
|
689
|
-
modelProvider: this.modelProvider,
|
690
|
-
modelName: this.modelName,
|
691
|
-
serverCount,
|
692
|
-
serverIdentifiers: this.connectors.map(connector => connector.publicIdentifier),
|
693
|
-
totalToolsAvailable: this._tools.length,
|
694
|
-
toolsAvailableNames: this._tools.map(t => t.name),
|
695
|
-
maxStepsConfigured: this.maxSteps,
|
696
|
-
memoryEnabled: this.memoryEnabled,
|
697
|
-
useServerManager: this.useServerManager,
|
698
|
-
maxStepsUsed: maxSteps ?? null,
|
699
|
-
manageConnector,
|
700
|
-
externalHistoryUsed: externalHistory !== undefined,
|
701
|
-
stepsTaken,
|
702
|
-
toolsUsedCount: toolsUsedNames.length,
|
703
|
-
toolsUsedNames,
|
704
|
-
response: result,
|
705
|
-
executionTimeMs,
|
706
|
-
errorType: success ? null : 'execution_error',
|
707
|
-
conversationHistoryLength,
|
708
|
-
});
|
709
|
-
if (manageConnector && !this.client && initializedHere) {
|
710
|
-
logger.info('๐งน Closing agent after query completion');
|
711
|
-
await this.close();
|
712
|
-
}
|
713
|
-
}
|
714
|
-
}
|
715
|
-
async close() {
|
716
|
-
// Delegate to remote agent if in remote mode
|
717
|
-
if (this.isRemote && this.remoteAgent) {
|
718
|
-
await this.remoteAgent.close();
|
719
|
-
return;
|
720
|
-
}
|
721
|
-
logger.info('๐ Closing MCPAgent resourcesโฆ');
|
722
|
-
// Shutdown observability handlers (important for serverless)
|
723
|
-
await this.observabilityManager.shutdown();
|
724
|
-
try {
|
725
|
-
this._agentExecutor = null;
|
726
|
-
this._tools = [];
|
727
|
-
if (this.client) {
|
728
|
-
logger.info('๐ Closing sessions through client');
|
729
|
-
await this.client.closeAllSessions();
|
730
|
-
this.sessions = {};
|
731
|
-
}
|
732
|
-
else {
|
733
|
-
for (const connector of this.connectors) {
|
734
|
-
logger.info('๐ Disconnecting connector');
|
735
|
-
await connector.disconnect();
|
736
|
-
}
|
737
|
-
}
|
738
|
-
if ('connectorToolMap' in this.adapter) {
|
739
|
-
this.adapter = new LangChainAdapter();
|
740
|
-
}
|
741
|
-
}
|
742
|
-
finally {
|
743
|
-
this._initialized = false;
|
744
|
-
logger.info('๐ Agent closed successfully');
|
745
|
-
}
|
746
|
-
}
|
747
|
-
/**
|
748
|
-
* Yields LangChain StreamEvent objects from the underlying streamEvents() method.
|
749
|
-
* This provides token-level streaming and fine-grained event updates.
|
750
|
-
*/
|
751
|
-
async *streamEvents(query, maxSteps, manageConnector = true, externalHistory) {
|
752
|
-
let initializedHere = false;
|
753
|
-
const startTime = Date.now();
|
754
|
-
let success = false;
|
755
|
-
let eventCount = 0;
|
756
|
-
let totalResponseLength = 0;
|
757
|
-
let finalResponse = '';
|
758
|
-
try {
|
759
|
-
// Initialize if needed
|
760
|
-
if (manageConnector && !this._initialized) {
|
761
|
-
await this.initialize();
|
762
|
-
initializedHere = true;
|
763
|
-
}
|
764
|
-
else if (!this._initialized && this.autoInitialize) {
|
765
|
-
await this.initialize();
|
766
|
-
initializedHere = true;
|
767
|
-
}
|
768
|
-
const agentExecutor = this.agentExecutor;
|
769
|
-
if (!agentExecutor) {
|
770
|
-
throw new Error('MCP agent failed to initialize');
|
771
|
-
}
|
772
|
-
// Set max iterations
|
773
|
-
const steps = maxSteps ?? this.maxSteps;
|
774
|
-
agentExecutor.maxIterations = steps;
|
775
|
-
const display_query = query.length > 50 ? `${query.slice(0, 50).replace(/\n/g, ' ')}...` : query.replace(/\n/g, ' ');
|
776
|
-
logger.info(`๐ฌ Received query for streamEvents: '${display_query}'`);
|
777
|
-
// Add user message to history if memory enabled
|
778
|
-
if (this.memoryEnabled) {
|
779
|
-
logger.info(`๐ Adding user message to history: ${query}`);
|
780
|
-
this.addToHistory(new HumanMessage(query));
|
781
|
-
}
|
782
|
-
// Prepare history
|
783
|
-
const historyToUse = externalHistory ?? this.conversationHistory;
|
784
|
-
const langchainHistory = [];
|
785
|
-
for (const msg of historyToUse) {
|
786
|
-
if (msg instanceof HumanMessage || msg instanceof AIMessage || msg instanceof ToolMessage) {
|
787
|
-
langchainHistory.push(msg);
|
788
|
-
}
|
789
|
-
else {
|
790
|
-
logger.info(`โ ๏ธ Skipped message of type: ${msg.constructor.name}`);
|
791
|
-
}
|
792
|
-
}
|
793
|
-
// Prepare inputs
|
794
|
-
const inputs = { input: query, chat_history: langchainHistory };
|
795
|
-
logger.info('callbacks', this.callbacks);
|
796
|
-
// Stream events from the agent executor
|
797
|
-
const eventStream = agentExecutor.streamEvents(inputs, {
|
798
|
-
version: 'v2',
|
799
|
-
callbacks: this.callbacks.length > 0 ? this.callbacks : undefined,
|
800
|
-
});
|
801
|
-
// Yield each event
|
802
|
-
for await (const event of eventStream) {
|
803
|
-
eventCount++;
|
804
|
-
// Skip null or invalid events
|
805
|
-
if (!event || typeof event !== 'object') {
|
806
|
-
continue;
|
807
|
-
}
|
808
|
-
// Track response length for telemetry
|
809
|
-
if (event.event === 'on_chat_model_stream' && event.data?.chunk?.content) {
|
810
|
-
totalResponseLength += event.data.chunk.content.length;
|
811
|
-
}
|
812
|
-
yield event;
|
813
|
-
// Capture final response from chain end event
|
814
|
-
if (event.event === 'on_chain_end' && event.data?.output) {
|
815
|
-
const output = event.data.output;
|
816
|
-
if (Array.isArray(output) && output.length > 0 && output[0]?.text) {
|
817
|
-
finalResponse = output[0].text;
|
818
|
-
}
|
819
|
-
}
|
820
|
-
}
|
821
|
-
// Add the final AI response to conversation history if memory is enabled
|
822
|
-
if (this.memoryEnabled && finalResponse) {
|
823
|
-
this.addToHistory(new AIMessage(finalResponse));
|
824
|
-
}
|
825
|
-
logger.info(`๐ StreamEvents complete - ${eventCount} events emitted`);
|
826
|
-
success = true;
|
827
|
-
}
|
828
|
-
catch (e) {
|
829
|
-
logger.error(`โ Error during streamEvents: ${e}`);
|
830
|
-
if (initializedHere && manageConnector) {
|
831
|
-
logger.info('๐งน Cleaning up resources after initialization error in streamEvents');
|
832
|
-
await this.close();
|
833
|
-
}
|
834
|
-
throw e;
|
835
|
-
}
|
836
|
-
finally {
|
837
|
-
// Track telemetry
|
838
|
-
const executionTimeMs = Date.now() - startTime;
|
839
|
-
let serverCount = 0;
|
840
|
-
if (this.client) {
|
841
|
-
serverCount = Object.keys(this.client.getAllActiveSessions()).length;
|
842
|
-
}
|
843
|
-
else if (this.connectors) {
|
844
|
-
serverCount = this.connectors.length;
|
845
|
-
}
|
846
|
-
const conversationHistoryLength = this.memoryEnabled ? this.conversationHistory.length : 0;
|
847
|
-
await this.telemetry.trackAgentExecution({
|
848
|
-
executionMethod: 'streamEvents',
|
849
|
-
query,
|
850
|
-
success,
|
851
|
-
modelProvider: this.modelProvider,
|
852
|
-
modelName: this.modelName,
|
853
|
-
serverCount,
|
854
|
-
serverIdentifiers: this.connectors.map(connector => connector.publicIdentifier),
|
855
|
-
totalToolsAvailable: this._tools.length,
|
856
|
-
toolsAvailableNames: this._tools.map(t => t.name),
|
857
|
-
maxStepsConfigured: this.maxSteps,
|
858
|
-
memoryEnabled: this.memoryEnabled,
|
859
|
-
useServerManager: this.useServerManager,
|
860
|
-
maxStepsUsed: maxSteps ?? null,
|
861
|
-
manageConnector,
|
862
|
-
externalHistoryUsed: externalHistory !== undefined,
|
863
|
-
response: `[STREAMED RESPONSE - ${totalResponseLength} chars]`,
|
864
|
-
executionTimeMs,
|
865
|
-
errorType: success ? null : 'streaming_error',
|
866
|
-
conversationHistoryLength,
|
867
|
-
});
|
868
|
-
// Clean up if needed
|
869
|
-
if (manageConnector && !this.client && initializedHere) {
|
870
|
-
logger.info('๐งน Closing agent after streamEvents completion');
|
871
|
-
await this.close();
|
872
|
-
}
|
873
|
-
}
|
874
|
-
}
|
875
|
-
/**
|
876
|
-
* Attempt to create structured output from raw result with validation and retry logic.
|
877
|
-
*/
|
878
|
-
async _attemptStructuredOutput(rawResult, structuredLlm, outputSchema, schemaDescription) {
|
879
|
-
logger.info(`๐ Attempting structured output with schema: ${outputSchema}`);
|
880
|
-
logger.info(`๐ Schema description: ${schemaDescription}`);
|
881
|
-
logger.info(`๐ Raw result: ${JSON.stringify(rawResult, null, 2)}`);
|
882
|
-
// Handle different input formats - rawResult might be an array or object from the agent
|
883
|
-
let textContent = '';
|
884
|
-
if (typeof rawResult === 'string') {
|
885
|
-
textContent = rawResult;
|
886
|
-
}
|
887
|
-
else if (rawResult && typeof rawResult === 'object') {
|
888
|
-
// Handle object format
|
889
|
-
textContent = JSON.stringify(rawResult);
|
890
|
-
}
|
891
|
-
// If we couldn't extract text, use the stringified version
|
892
|
-
if (!textContent) {
|
893
|
-
textContent = JSON.stringify(rawResult);
|
894
|
-
}
|
895
|
-
// Get detailed schema information for better prompting
|
896
|
-
const maxRetries = 3;
|
897
|
-
let lastError = '';
|
898
|
-
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
899
|
-
logger.info(`๐ Structured output attempt ${attempt}/${maxRetries}`);
|
900
|
-
let formatPrompt = `
|
901
|
-
Please format the following information according to the EXACT schema specified below.
|
902
|
-
You must use the exact field names and types as shown in the schema.
|
903
|
-
|
904
|
-
Required schema format:
|
905
|
-
${schemaDescription}
|
906
|
-
|
907
|
-
Content to extract from:
|
908
|
-
${textContent}
|
909
|
-
|
910
|
-
IMPORTANT:
|
911
|
-
- Use ONLY the field names specified in the schema
|
912
|
-
- Match the data types exactly (string, number, boolean, array, etc.)
|
913
|
-
- Include ALL required fields
|
914
|
-
- Return valid JSON that matches the schema structure exactly
|
915
|
-
`;
|
916
|
-
// Add specific error feedback for retry attempts
|
917
|
-
if (attempt > 1) {
|
918
|
-
formatPrompt += `
|
919
|
-
|
920
|
-
PREVIOUS ATTEMPT FAILED with error: ${lastError}
|
921
|
-
Please fix the issues mentioned above and ensure the output matches the schema exactly.
|
922
|
-
`;
|
923
|
-
}
|
924
|
-
try {
|
925
|
-
const structuredResult = await structuredLlm.invoke(formatPrompt);
|
926
|
-
logger.info(`๐ Structured result attempt ${attempt}: ${JSON.stringify(structuredResult, null, 2)}`);
|
927
|
-
// Validate the structured result
|
928
|
-
const validatedResult = this._validateStructuredResult(structuredResult, outputSchema);
|
929
|
-
logger.info(`โ
Structured output successful on attempt ${attempt}`);
|
930
|
-
return validatedResult;
|
931
|
-
}
|
932
|
-
catch (e) {
|
933
|
-
lastError = e instanceof Error ? e.message : String(e);
|
934
|
-
logger.warn(`โ ๏ธ Structured output attempt ${attempt} failed: ${lastError}`);
|
935
|
-
if (attempt === maxRetries) {
|
936
|
-
logger.error(`โ All ${maxRetries} structured output attempts failed`);
|
937
|
-
throw new Error(`Failed to generate valid structured output after ${maxRetries} attempts. Last error: ${lastError}`);
|
938
|
-
}
|
939
|
-
// Continue to next attempt
|
940
|
-
continue;
|
941
|
-
}
|
942
|
-
}
|
943
|
-
// This should never be reached, but TypeScript requires it
|
944
|
-
throw new Error('Unexpected error in structured output generation');
|
945
|
-
}
|
946
|
-
/**
|
947
|
-
* Validate the structured result against the schema with detailed error reporting
|
948
|
-
*/
|
949
|
-
_validateStructuredResult(structuredResult, outputSchema) {
|
950
|
-
// Use Zod to validate the structured result
|
951
|
-
try {
|
952
|
-
// Use Zod to validate the structured result
|
953
|
-
const validatedResult = outputSchema.parse(structuredResult);
|
954
|
-
// Additional validation for required fields
|
955
|
-
const schemaType = outputSchema;
|
956
|
-
if (schemaType._def && schemaType._def.shape) {
|
957
|
-
for (const [fieldName, fieldSchema] of Object.entries(schemaType._def.shape)) {
|
958
|
-
const field = fieldSchema;
|
959
|
-
const isOptional = field.isOptional?.() ?? field._def?.typeName === 'ZodOptional';
|
960
|
-
const isNullable = field.isNullable?.() ?? field._def?.typeName === 'ZodNullable';
|
961
|
-
if (!isOptional && !isNullable) {
|
962
|
-
const value = validatedResult[fieldName];
|
963
|
-
if (value === null || value === undefined
|
964
|
-
|| (typeof value === 'string' && !value.trim())
|
965
|
-
|| (Array.isArray(value) && value.length === 0)) {
|
966
|
-
throw new Error(`Required field '${fieldName}' is missing or empty`);
|
967
|
-
}
|
968
|
-
}
|
969
|
-
}
|
970
|
-
}
|
971
|
-
return validatedResult;
|
972
|
-
}
|
973
|
-
catch (e) {
|
974
|
-
logger.debug(`Validation details: ${e}`);
|
975
|
-
throw e; // Re-raise to trigger retry logic
|
976
|
-
}
|
977
|
-
}
|
978
|
-
/**
|
979
|
-
* Enhance the query with schema information to make the agent aware of required fields.
|
980
|
-
*/
|
981
|
-
_enhanceQueryWithSchema(query, outputSchema) {
|
982
|
-
try {
|
983
|
-
const schemaDescription = JSON.stringify(zodToJsonSchema(outputSchema), null, 2);
|
984
|
-
// Enhance the query with schema awareness
|
985
|
-
const enhancedQuery = `
|
986
|
-
${query}
|
987
|
-
|
988
|
-
IMPORTANT: Your response must include sufficient information to populate the following structured output:
|
989
|
-
|
990
|
-
${schemaDescription}
|
991
|
-
|
992
|
-
Make sure you gather ALL the required information during your task execution.
|
993
|
-
If any required information is missing, continue working to find it.
|
994
|
-
`;
|
995
|
-
return enhancedQuery;
|
996
|
-
}
|
997
|
-
catch (e) {
|
998
|
-
logger.warn(`Could not extract schema details: ${e}`);
|
999
|
-
return query;
|
1000
|
-
}
|
1001
|
-
}
|
1002
|
-
}
|