@librechat/agents 3.1.21 → 3.1.23

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.
Files changed (32) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +33 -8
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/graphs/Graph.cjs +3 -2
  4. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  5. package/dist/cjs/main.cjs +6 -6
  6. package/dist/cjs/tools/ToolNode.cjs +45 -12
  7. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  8. package/dist/cjs/tools/{createSchemaOnlyTool.cjs → schema.cjs} +1 -1
  9. package/dist/cjs/tools/schema.cjs.map +1 -0
  10. package/dist/esm/agents/AgentContext.mjs +33 -8
  11. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  12. package/dist/esm/graphs/Graph.mjs +2 -1
  13. package/dist/esm/graphs/Graph.mjs.map +1 -1
  14. package/dist/esm/main.mjs +1 -1
  15. package/dist/esm/tools/ToolNode.mjs +45 -12
  16. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  17. package/dist/esm/tools/{createSchemaOnlyTool.mjs → schema.mjs} +1 -1
  18. package/dist/esm/tools/schema.mjs.map +1 -0
  19. package/dist/types/agents/AgentContext.d.ts +6 -1
  20. package/dist/types/index.d.ts +1 -1
  21. package/dist/types/tools/ToolNode.d.ts +3 -1
  22. package/dist/types/types/tools.d.ts +6 -0
  23. package/package.json +1 -1
  24. package/src/agents/AgentContext.ts +40 -9
  25. package/src/graphs/Graph.ts +2 -1
  26. package/src/index.ts +1 -1
  27. package/src/tools/ToolNode.ts +55 -13
  28. package/src/types/tools.ts +6 -0
  29. package/dist/cjs/tools/createSchemaOnlyTool.cjs.map +0 -1
  30. package/dist/esm/tools/createSchemaOnlyTool.mjs.map +0 -1
  31. /package/dist/types/tools/{createSchemaOnlyTool.d.ts → schema.d.ts} +0 -0
  32. /package/src/tools/{createSchemaOnlyTool.ts → schema.ts} +0 -0
@@ -9,7 +9,7 @@ export * from './tools/CodeExecutor';
9
9
  export * from './tools/ProgrammaticToolCalling';
10
10
  export * from './tools/ToolSearch';
11
11
  export * from './tools/ToolNode';
12
- export * from './tools/createSchemaOnlyTool';
12
+ export * from './tools/schema';
13
13
  export * from './tools/handlers';
14
14
  export * from './tools/search';
15
15
  export * from './common';
@@ -22,7 +22,9 @@ export declare class ToolNode<T = any> extends RunnableCallable<T, T> {
22
22
  private eventDrivenMode;
23
23
  /** Tool definitions for event-driven mode */
24
24
  private toolDefinitions?;
25
- constructor({ tools, toolMap, name, tags, errorHandler, toolCallStepIds, handleToolErrors, loadRuntimeTools, toolRegistry, sessions, eventDrivenMode, toolDefinitions, }: t.ToolNodeConstructorParams);
25
+ /** Agent ID for event-driven mode */
26
+ private agentId?;
27
+ constructor({ tools, toolMap, name, tags, errorHandler, toolCallStepIds, handleToolErrors, loadRuntimeTools, toolRegistry, sessions, eventDrivenMode, toolDefinitions, agentId, }: t.ToolNodeConstructorParams);
26
28
  /**
27
29
  * Returns cached programmatic tools, computing once on first access.
28
30
  * Single iteration builds both toolMap and toolDefs simultaneously.
@@ -35,6 +35,8 @@ export type ToolNodeOptions = {
35
35
  eventDrivenMode?: boolean;
36
36
  /** Tool definitions for event-driven mode (used for context, not invocation) */
37
37
  toolDefinitions?: Map<string, LCTool>;
38
+ /** Agent ID for event-driven mode (used to identify which agent's context to use) */
39
+ agentId?: string;
38
40
  };
39
41
  export type ToolNodeConstructorParams = ToolRefs & ToolNodeOptions;
40
42
  export type ToolEndEvent = {
@@ -128,6 +130,10 @@ export type ToolExecuteBatchRequest = {
128
130
  userId?: string;
129
131
  /** Agent ID for context */
130
132
  agentId?: string;
133
+ /** Runtime configurable from RunnableConfig (includes user, userMCPAuthMap, etc.) */
134
+ configurable?: Record<string, unknown>;
135
+ /** Runtime metadata from RunnableConfig (includes thread_id, run_id, provider, etc.) */
136
+ metadata?: Record<string, unknown>;
131
137
  /** Promise resolver - handler calls this with ALL results */
132
138
  resolve: (results: ToolExecuteResult[]) => void;
133
139
  /** Promise rejector - handler calls this on fatal error */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@librechat/agents",
3
- "version": "3.1.21",
3
+ "version": "3.1.23",
4
4
  "main": "./dist/cjs/main.cjs",
5
5
  "module": "./dist/esm/main.mjs",
6
6
  "types": "./dist/types/index.d.ts",
@@ -10,6 +10,7 @@ import type {
10
10
  import type { RunnableConfig, Runnable } from '@langchain/core/runnables';
11
11
  import type * as t from '@/types';
12
12
  import type { createPruneMessages } from '@/messages';
13
+ import { createSchemaOnlyTools } from '@/tools/schema';
13
14
  import { ContentTypes, Providers } from '@/common';
14
15
  import { toJsonSchema } from '@/utils/schema';
15
16
 
@@ -571,40 +572,70 @@ export class AgentContext {
571
572
 
572
573
  /**
573
574
  * Gets tools that should be bound to the LLM.
574
- * Includes:
575
+ * In event-driven mode (toolDefinitions present, tools empty), creates schema-only tools.
576
+ * Otherwise filters tool instances based on:
575
577
  * 1. Non-deferred tools with allowed_callers: ['direct']
576
578
  * 2. Discovered tools (from tool search)
577
579
  * @returns Array of tools to bind to model
578
580
  */
579
581
  getToolsForBinding(): t.GraphTools | undefined {
582
+ /** Event-driven mode: create schema-only tools from definitions */
583
+ if (this.toolDefinitions && this.toolDefinitions.length > 0) {
584
+ return this.getEventDrivenToolsForBinding();
585
+ }
586
+
587
+ /** Traditional mode: filter actual tool instances */
580
588
  if (!this.tools || !this.toolRegistry) {
581
589
  return this.tools;
582
590
  }
583
591
 
584
- const toolsToInclude = this.tools.filter((tool) => {
592
+ return this.filterToolsForBinding(this.tools);
593
+ }
594
+
595
+ /** Creates schema-only tools from toolDefinitions for event-driven mode */
596
+ private getEventDrivenToolsForBinding(): t.GraphTools {
597
+ if (!this.toolDefinitions) {
598
+ return [];
599
+ }
600
+
601
+ const defsToInclude = this.toolDefinitions.filter((def) => {
602
+ const allowedCallers = def.allowed_callers ?? ['direct'];
603
+ if (!allowedCallers.includes('direct')) {
604
+ return false;
605
+ }
606
+ if (
607
+ def.defer_loading === true &&
608
+ !this.discoveredToolNames.has(def.name)
609
+ ) {
610
+ return false;
611
+ }
612
+ return true;
613
+ });
614
+
615
+ return createSchemaOnlyTools(defsToInclude) as t.GraphTools;
616
+ }
617
+
618
+ /** Filters tool instances for binding based on registry config */
619
+ private filterToolsForBinding(tools: t.GraphTools): t.GraphTools {
620
+ return tools.filter((tool) => {
585
621
  if (!('name' in tool)) {
586
- return true; // No name, include by default
622
+ return true;
587
623
  }
588
624
 
589
625
  const toolDef = this.toolRegistry?.get(tool.name);
590
626
  if (!toolDef) {
591
- return true; // Not in registry, include by default
627
+ return true;
592
628
  }
593
629
 
594
- // Check if discovered (overrides defer_loading)
595
630
  if (this.discoveredToolNames.has(tool.name)) {
596
- // Discovered tools must still have allowed_callers: ['direct']
597
631
  const allowedCallers = toolDef.allowed_callers ?? ['direct'];
598
632
  return allowedCallers.includes('direct');
599
633
  }
600
634
 
601
- // Not discovered: must be direct-callable AND not deferred
602
635
  const allowedCallers = toolDef.allowed_callers ?? ['direct'];
603
636
  return (
604
637
  allowedCallers.includes('direct') && toolDef.defer_loading !== true
605
638
  );
606
639
  });
607
-
608
- return toolsToInclude;
609
640
  }
610
641
  }
@@ -58,9 +58,9 @@ import {
58
58
  } from '@/utils';
59
59
  import { getChatModelClass, manualToolStreamProviders } from '@/llm/providers';
60
60
  import { ToolNode as CustomToolNode, toolsCondition } from '@/tools/ToolNode';
61
- import { createSchemaOnlyTools } from '@/tools/createSchemaOnlyTool';
62
61
  import { ChatOpenAI, AzureChatOpenAI } from '@/llm/openai';
63
62
  import { safeDispatchCustomEvent } from '@/utils/events';
63
+ import { createSchemaOnlyTools } from '@/tools/schema';
64
64
  import { AgentContext } from '@/agents/AgentContext';
65
65
  import { createFakeStreamingLLM } from '@/llm/fake';
66
66
  import { HandlerRegistry } from '@/events';
@@ -472,6 +472,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
472
472
  sessions: this.sessions,
473
473
  eventDrivenMode: true,
474
474
  toolDefinitions: toolDefMap,
475
+ agentId: agentContext?.agentId,
475
476
  });
476
477
  }
477
478
 
package/src/index.ts CHANGED
@@ -14,7 +14,7 @@ export * from './tools/CodeExecutor';
14
14
  export * from './tools/ProgrammaticToolCalling';
15
15
  export * from './tools/ToolSearch';
16
16
  export * from './tools/ToolNode';
17
- export * from './tools/createSchemaOnlyTool';
17
+ export * from './tools/schema';
18
18
  export * from './tools/handlers';
19
19
  export * from './tools/search';
20
20
 
@@ -49,6 +49,8 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
49
49
  private eventDrivenMode: boolean = false;
50
50
  /** Tool definitions for event-driven mode */
51
51
  private toolDefinitions?: Map<string, t.LCTool>;
52
+ /** Agent ID for event-driven mode */
53
+ private agentId?: string;
52
54
 
53
55
  constructor({
54
56
  tools,
@@ -63,6 +65,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
63
65
  sessions,
64
66
  eventDrivenMode,
65
67
  toolDefinitions,
68
+ agentId,
66
69
  }: t.ToolNodeConstructorParams) {
67
70
  super({ name, tags, func: (input, config) => this.run(input, config) });
68
71
  this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));
@@ -75,6 +78,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
75
78
  this.sessions = sessions;
76
79
  this.eventDrivenMode = eventDrivenMode ?? false;
77
80
  this.toolDefinitions = toolDefinitions;
81
+ this.agentId = agentId;
78
82
  }
79
83
 
80
84
  /**
@@ -279,7 +283,11 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
279
283
  const request: t.ToolExecuteBatchRequest = {
280
284
  toolCalls: requests,
281
285
  userId: config.configurable?.user_id as string | undefined,
282
- agentId: config.configurable?.agent_id as string | undefined,
286
+ agentId: this.agentId,
287
+ configurable: config.configurable as
288
+ | Record<string, unknown>
289
+ | undefined,
290
+ metadata: config.metadata as Record<string, unknown> | undefined,
283
291
  resolve,
284
292
  reject,
285
293
  };
@@ -289,27 +297,61 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
289
297
  );
290
298
 
291
299
  const outputs: ToolMessage[] = results.map((result) => {
292
- const toolName =
293
- requests.find((r) => r.id === result.toolCallId)?.name ?? 'unknown';
300
+ const request = requests.find((r) => r.id === result.toolCallId);
301
+ const toolName = request?.name ?? 'unknown';
302
+ const stepId = this.toolCallStepIds?.get(result.toolCallId) ?? '';
303
+
304
+ let toolMessage: ToolMessage;
305
+ let contentString: string;
294
306
 
295
307
  if (result.status === 'error') {
296
- return new ToolMessage({
308
+ contentString = `Error: ${result.errorMessage ?? 'Unknown error'}\n Please fix your mistakes.`;
309
+ toolMessage = new ToolMessage({
297
310
  status: 'error',
298
- content: `Error: ${result.errorMessage ?? 'Unknown error'}\n Please fix your mistakes.`,
311
+ content: contentString,
299
312
  name: toolName,
300
313
  tool_call_id: result.toolCallId,
301
314
  });
302
- }
303
-
304
- return new ToolMessage({
305
- status: 'success',
306
- content:
315
+ } else {
316
+ contentString =
307
317
  typeof result.content === 'string'
308
318
  ? result.content
309
- : JSON.stringify(result.content),
319
+ : JSON.stringify(result.content);
320
+ toolMessage = new ToolMessage({
321
+ status: 'success',
322
+ content: contentString,
323
+ name: toolName,
324
+ tool_call_id: result.toolCallId,
325
+ });
326
+ }
327
+
328
+ const tool_call: t.ProcessedToolCall = {
329
+ args:
330
+ typeof request?.args === 'string'
331
+ ? request.args
332
+ : JSON.stringify(request?.args ?? {}),
310
333
  name: toolName,
311
- tool_call_id: result.toolCallId,
312
- });
334
+ id: result.toolCallId,
335
+ output: contentString,
336
+ progress: 1,
337
+ };
338
+
339
+ const runStepCompletedData = {
340
+ result: {
341
+ id: stepId,
342
+ index: request?.turn ?? 0,
343
+ type: 'tool_call' as const,
344
+ tool_call,
345
+ },
346
+ };
347
+
348
+ safeDispatchCustomEvent(
349
+ GraphEvents.ON_RUN_STEP_COMPLETED,
350
+ runStepCompletedData,
351
+ config
352
+ );
353
+
354
+ return toolMessage;
313
355
  });
314
356
 
315
357
  return (Array.isArray(input) ? outputs : { messages: outputs }) as T;
@@ -45,6 +45,8 @@ export type ToolNodeOptions = {
45
45
  eventDrivenMode?: boolean;
46
46
  /** Tool definitions for event-driven mode (used for context, not invocation) */
47
47
  toolDefinitions?: Map<string, LCTool>;
48
+ /** Agent ID for event-driven mode (used to identify which agent's context to use) */
49
+ agentId?: string;
48
50
  };
49
51
 
50
52
  export type ToolNodeConstructorParams = ToolRefs & ToolNodeOptions;
@@ -159,6 +161,10 @@ export type ToolExecuteBatchRequest = {
159
161
  userId?: string;
160
162
  /** Agent ID for context */
161
163
  agentId?: string;
164
+ /** Runtime configurable from RunnableConfig (includes user, userMCPAuthMap, etc.) */
165
+ configurable?: Record<string, unknown>;
166
+ /** Runtime metadata from RunnableConfig (includes thread_id, run_id, provider, etc.) */
167
+ metadata?: Record<string, unknown>;
162
168
  /** Promise resolver - handler calls this with ALL results */
163
169
  resolve: (results: ToolExecuteResult[]) => void;
164
170
  /** Promise rejector - handler calls this on fatal error */
@@ -1 +0,0 @@
1
- {"version":3,"file":"createSchemaOnlyTool.cjs","sources":["../../../src/tools/createSchemaOnlyTool.ts"],"sourcesContent":["import { tool, type StructuredToolInterface } from '@langchain/core/tools';\nimport type { LCTool } from '@/types';\n\n/**\n * Creates a schema-only tool for LLM binding in event-driven mode.\n * These tools have valid schemas for the LLM to understand but should\n * never be invoked directly - ToolNode handles execution via events.\n */\nexport function createSchemaOnlyTool(\n definition: LCTool\n): StructuredToolInterface {\n const { name, description, parameters, responseFormat } = definition;\n\n return tool(\n async () => {\n throw new Error(\n `Tool \"${name}\" should not be invoked directly in event-driven mode. ` +\n 'ToolNode should dispatch ON_TOOL_EXECUTE events instead.'\n );\n },\n {\n name,\n description: description ?? '',\n schema: parameters ?? { type: 'object', properties: {} },\n responseFormat: responseFormat ?? 'content_and_artifact',\n }\n );\n}\n\n/**\n * Creates schema-only tools for all definitions in an array.\n */\nexport function createSchemaOnlyTools(\n definitions: LCTool[]\n): StructuredToolInterface[] {\n return definitions.map((def) => createSchemaOnlyTool(def));\n}\n"],"names":["tool"],"mappings":";;;;AAGA;;;;AAIG;AACG,SAAU,oBAAoB,CAClC,UAAkB,EAAA;IAElB,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,GAAG,UAAU;AAEpE,IAAA,OAAOA,UAAI,CACT,YAAW;AACT,QAAA,MAAM,IAAI,KAAK,CACb,CAAA,MAAA,EAAS,IAAI,CAAyD,uDAAA,CAAA;AACpE,YAAA,0DAA0D,CAC7D;AACH,KAAC,EACD;QACE,IAAI;QACJ,WAAW,EAAE,WAAW,IAAI,EAAE;QAC9B,MAAM,EAAE,UAAU,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE;QACxD,cAAc,EAAE,cAAc,IAAI,sBAAsB;AACzD,KAAA,CACF;AACH;AAEA;;AAEG;AACG,SAAU,qBAAqB,CACnC,WAAqB,EAAA;AAErB,IAAA,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,oBAAoB,CAAC,GAAG,CAAC,CAAC;AAC5D;;;;;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"createSchemaOnlyTool.mjs","sources":["../../../src/tools/createSchemaOnlyTool.ts"],"sourcesContent":["import { tool, type StructuredToolInterface } from '@langchain/core/tools';\nimport type { LCTool } from '@/types';\n\n/**\n * Creates a schema-only tool for LLM binding in event-driven mode.\n * These tools have valid schemas for the LLM to understand but should\n * never be invoked directly - ToolNode handles execution via events.\n */\nexport function createSchemaOnlyTool(\n definition: LCTool\n): StructuredToolInterface {\n const { name, description, parameters, responseFormat } = definition;\n\n return tool(\n async () => {\n throw new Error(\n `Tool \"${name}\" should not be invoked directly in event-driven mode. ` +\n 'ToolNode should dispatch ON_TOOL_EXECUTE events instead.'\n );\n },\n {\n name,\n description: description ?? '',\n schema: parameters ?? { type: 'object', properties: {} },\n responseFormat: responseFormat ?? 'content_and_artifact',\n }\n );\n}\n\n/**\n * Creates schema-only tools for all definitions in an array.\n */\nexport function createSchemaOnlyTools(\n definitions: LCTool[]\n): StructuredToolInterface[] {\n return definitions.map((def) => createSchemaOnlyTool(def));\n}\n"],"names":[],"mappings":";;AAGA;;;;AAIG;AACG,SAAU,oBAAoB,CAClC,UAAkB,EAAA;IAElB,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,GAAG,UAAU;AAEpE,IAAA,OAAO,IAAI,CACT,YAAW;AACT,QAAA,MAAM,IAAI,KAAK,CACb,CAAA,MAAA,EAAS,IAAI,CAAyD,uDAAA,CAAA;AACpE,YAAA,0DAA0D,CAC7D;AACH,KAAC,EACD;QACE,IAAI;QACJ,WAAW,EAAE,WAAW,IAAI,EAAE;QAC9B,MAAM,EAAE,UAAU,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE;QACxD,cAAc,EAAE,cAAc,IAAI,sBAAsB;AACzD,KAAA,CACF;AACH;AAEA;;AAEG;AACG,SAAU,qBAAqB,CACnC,WAAqB,EAAA;AAErB,IAAA,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,oBAAoB,CAAC,GAAG,CAAC,CAAC;AAC5D;;;;"}