langchain 1.5.5 → 1.5.7
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/CHANGELOG.md +18 -0
- package/dist/agents/errors.cjs +6 -0
- package/dist/agents/errors.cjs.map +1 -1
- package/dist/agents/errors.d.cts +2 -0
- package/dist/agents/errors.d.cts.map +1 -1
- package/dist/agents/errors.d.ts +2 -0
- package/dist/agents/errors.d.ts.map +1 -1
- package/dist/agents/errors.js +6 -0
- package/dist/agents/errors.js.map +1 -1
- package/dist/agents/middleware/constants.cjs +3 -0
- package/dist/agents/middleware/constants.cjs.map +1 -1
- package/dist/agents/middleware/constants.js +3 -1
- package/dist/agents/middleware/constants.js.map +1 -1
- package/dist/agents/middleware/llmToolSelector.cjs +2 -0
- package/dist/agents/middleware/llmToolSelector.cjs.map +1 -1
- package/dist/agents/middleware/llmToolSelector.d.cts.map +1 -1
- package/dist/agents/middleware/llmToolSelector.d.ts.map +1 -1
- package/dist/agents/middleware/llmToolSelector.js +2 -0
- package/dist/agents/middleware/llmToolSelector.js.map +1 -1
- package/dist/agents/middleware/summarization.cjs +6 -2
- package/dist/agents/middleware/summarization.cjs.map +1 -1
- package/dist/agents/middleware/summarization.d.cts.map +1 -1
- package/dist/agents/middleware/summarization.d.ts.map +1 -1
- package/dist/agents/middleware/summarization.js +6 -2
- package/dist/agents/middleware/summarization.js.map +1 -1
- package/dist/agents/middleware/toolEmulator.cjs +8 -1
- package/dist/agents/middleware/toolEmulator.cjs.map +1 -1
- package/dist/agents/middleware/toolEmulator.d.cts.map +1 -1
- package/dist/agents/middleware/toolEmulator.d.ts.map +1 -1
- package/dist/agents/middleware/toolEmulator.js +8 -1
- package/dist/agents/middleware/toolEmulator.js.map +1 -1
- package/dist/agents/tests/utils.cjs.map +1 -1
- package/dist/agents/tests/utils.js.map +1 -1
- package/dist/chat_models/universal.cjs +23 -1
- package/dist/chat_models/universal.cjs.map +1 -1
- package/dist/chat_models/universal.d.cts +4 -0
- package/dist/chat_models/universal.d.cts.map +1 -1
- package/dist/chat_models/universal.d.ts +4 -0
- package/dist/chat_models/universal.d.ts.map +1 -1
- package/dist/chat_models/universal.js +23 -1
- package/dist/chat_models/universal.js.map +1 -1
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# langchain
|
|
2
2
|
|
|
3
|
+
## 1.5.7
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#11363](https://github.com/langchain-ai/langchainjs/pull/11363) [`2d1f744`](https://github.com/langchain-ai/langchainjs/commit/2d1f7449c95a554d429535abae0115b4f578bbac) Thanks [@hntrl](https://github.com/hntrl)! - feat(langchain): add langsmith gateway to initChatModel- [#11362](https://github.com/langchain-ai/langchainjs/issues/11362)
|
|
8
|
+
|
|
9
|
+
## 1.5.6
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#11331](https://github.com/langchain-ai/langchainjs/pull/11331) [`18765b0`](https://github.com/langchain-ai/langchainjs/commit/18765b002c3819bc4dc42123a293cab27a35ce4f) Thanks [@thushanth-bengre-langchain](https://github.com/thushanth-bengre-langchain)! - fix(langchain): exclude middleware-internal model calls from the message projection
|
|
14
|
+
|
|
15
|
+
Bookkeeping model calls made by `summarizationMiddleware` and `toolEmulatorMiddleware` no longer appear in `run.messages` or `stream({ streamMode: "messages" })`, and the summary `summarizationMiddleware` writes back to state is no longer projected as a new message. These calls remain observable via `streamEvents({ version: "v2" })`, identified by `lc_source`.
|
|
16
|
+
|
|
17
|
+
- [#11344](https://github.com/langchain-ai/langchainjs/pull/11344) [`f08e0c6`](https://github.com/langchain-ai/langchainjs/commit/f08e0c6d50156accf95a36469a3e107a5598a3a0) Thanks [@hntrl](https://github.com/hntrl)! - fix: apply [Symbol.hasInstance] method to all comparable properties using .isInstance()
|
|
18
|
+
|
|
19
|
+
We have some internal schemas that rely on `z.instanceof()`. This uses a strict `instanceof` check which can conflict if there are multiple versions of core installed. This overrides the [Symbol.hasInstance](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/hasInstance) method to use the same logic as `.isInstance()` to compare objects at runtime.
|
|
20
|
+
|
|
3
21
|
## 1.5.5
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/dist/agents/errors.cjs
CHANGED
|
@@ -49,6 +49,9 @@ var ToolInvocationError = class extends Error {
|
|
|
49
49
|
static isInstance(error) {
|
|
50
50
|
return error instanceof Error && "~brand" in error && error["~brand"] === "ToolInvocationError";
|
|
51
51
|
}
|
|
52
|
+
static [Symbol.hasInstance](obj) {
|
|
53
|
+
return this.isInstance(obj);
|
|
54
|
+
}
|
|
52
55
|
};
|
|
53
56
|
/**
|
|
54
57
|
* Error thrown when a middleware fails.
|
|
@@ -84,6 +87,9 @@ var MiddlewareError = class MiddlewareError extends Error {
|
|
|
84
87
|
static isInstance(error) {
|
|
85
88
|
return error instanceof Error && "~brand" in error && error["~brand"] === "MiddlewareError";
|
|
86
89
|
}
|
|
90
|
+
static [Symbol.hasInstance](obj) {
|
|
91
|
+
return this.isInstance(obj);
|
|
92
|
+
}
|
|
87
93
|
};
|
|
88
94
|
//#endregion
|
|
89
95
|
exports.MiddlewareError = MiddlewareError;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.cjs","names":[],"sources":["../../src/agents/errors.ts"],"sourcesContent":["/* oxlint-disable no-instanceof/no-instanceof */\nimport type { ToolCall } from \"@langchain/core/messages/tool\";\nimport { isGraphBubbleUp } from \"@langchain/langgraph\";\n\nexport class MultipleToolsBoundError extends Error {\n constructor() {\n super(\n \"The provided LLM already has bound tools. \" +\n \"Please provide an LLM without bound tools to createAgent. \" +\n \"The agent will bind the tools provided in the 'tools' parameter.\"\n );\n }\n}\n\n/**\n * Raised when model returns multiple structured output tool calls when only one is expected.\n */\nexport class MultipleStructuredOutputsError extends Error {\n public readonly toolNames: string[];\n\n constructor(toolNames: string[]) {\n super(\n `The model has called multiple tools: ${toolNames.join(\n \", \"\n )} to return a structured output. ` +\n \"This is not supported. Please provide a single structured output.\"\n );\n this.toolNames = toolNames;\n }\n}\n\n/**\n * Raised when structured output tool call arguments fail to parse according to the schema.\n */\nexport class StructuredOutputParsingError extends Error {\n public readonly toolName: string;\n\n public readonly errors: string[];\n\n constructor(toolName: string, errors: string[]) {\n super(\n `Failed to parse structured output for tool '${toolName}':${errors\n .map((e) => `\\n - ${e}`)\n .join(\"\")}.`\n );\n this.toolName = toolName;\n this.errors = errors;\n }\n}\n\n/**\n * Raised when a tool call is throwing an error.\n */\nexport class ToolInvocationError extends Error {\n readonly \"~brand\" = \"ToolInvocationError\";\n\n public readonly toolCall: ToolCall;\n\n public readonly toolError: Error;\n\n constructor(toolError: unknown, toolCall: ToolCall) {\n const error =\n toolError instanceof Error ? toolError : new Error(String(toolError));\n const toolArgs = JSON.stringify(toolCall.args);\n super(\n `Error invoking tool '${toolCall.name}' with kwargs ${toolArgs} with error: ${error.stack}\\n Please fix the error and try again.`\n );\n\n this.toolCall = toolCall;\n this.toolError = error;\n }\n\n /**\n * Check if the error is a ToolInvocationError.\n * @param error - The error to check\n * @returns Whether the error is a ToolInvocationError\n */\n static isInstance(error: unknown): error is ToolInvocationError {\n return (\n error instanceof Error &&\n \"~brand\" in error &&\n error[\"~brand\"] === \"ToolInvocationError\"\n );\n }\n}\n\n/**\n * Error thrown when a middleware fails.\n *\n * Use `MiddlewareError.wrap()` to create instances. The constructor is private\n * to ensure that GraphBubbleUp errors (like GraphInterrupt) are never wrapped.\n */\nexport class MiddlewareError extends Error {\n readonly \"~brand\" = \"MiddlewareError\";\n\n private constructor(error: unknown, middlewareName: string) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n super(errorMessage);\n this.name =\n error instanceof Error\n ? error.name\n : `${middlewareName[0].toUpperCase() + middlewareName.slice(1)}Error`;\n\n if (error instanceof Error) {\n this.cause = error;\n }\n }\n\n /**\n * Wrap an error in a MiddlewareError, unless it's a GraphBubbleUp error\n * (like GraphInterrupt) which should propagate unchanged.\n *\n * @param error - The error to wrap\n * @param middlewareName - The name of the middleware that threw the error\n * @returns The original error if it's a GraphBubbleUp, otherwise a new MiddlewareError\n */\n static wrap(error: unknown, middlewareName: string): Error {\n // Don't wrap GraphBubbleUp errors (GraphInterrupt, NodeInterrupt, etc.)\n // These are control flow mechanisms that need to bubble up unchanged\n if (isGraphBubbleUp(error)) {\n return error;\n }\n return new MiddlewareError(error, middlewareName);\n }\n\n /**\n * Check if the error is a MiddlewareError.\n * @param error - The error to check\n * @returns Whether the error is a MiddlewareError\n */\n static isInstance(error: unknown): error is MiddlewareError {\n return (\n error instanceof Error &&\n \"~brand\" in error &&\n error[\"~brand\"] === \"MiddlewareError\"\n );\n }\n}\n"],"mappings":";;AAIA,IAAa,0BAAb,cAA6C,MAAM;CACjD,cAAc;EACZ,MACE,sKAGF;CACF;AACF;;;;AAKA,IAAa,iCAAb,cAAoD,MAAM;CACxD;CAEA,YAAY,WAAqB;EAC/B,MACE,wCAAwC,UAAU,KAChD,IACF,EAAE,kGAEJ;EACA,KAAK,YAAY;CACnB;AACF;;;;AAKA,IAAa,+BAAb,cAAkD,MAAM;CACtD;CAEA;CAEA,YAAY,UAAkB,QAAkB;EAC9C,MACE,+CAA+C,SAAS,IAAI,OACzD,KAAK,MAAM,SAAS,GAAG,CAAC,CACxB,KAAK,EAAE,EAAE,EACd;EACA,KAAK,WAAW;EAChB,KAAK,SAAS;CAChB;AACF;;;;AAKA,IAAa,sBAAb,cAAyC,MAAM;CAC7C,WAAoB;CAEpB;CAEA;CAEA,YAAY,WAAoB,UAAoB;EAClD,MAAM,QACJ,qBAAqB,QAAQ,YAAY,IAAI,MAAM,OAAO,SAAS,CAAC;EACtE,MAAM,WAAW,KAAK,UAAU,SAAS,IAAI;EAC7C,MACE,wBAAwB,SAAS,KAAK,gBAAgB,SAAS,eAAe,MAAM,MAAM,uCAC5F;EAEA,KAAK,WAAW;EAChB,KAAK,YAAY;CACnB;;;;;;CAOA,OAAO,WAAW,OAA8C;EAC9D,OACE,iBAAiB,SACjB,YAAY,SACZ,MAAM,cAAc;CAExB;AACF;;;;;;;AAQA,IAAa,kBAAb,MAAa,wBAAwB,MAAM;CACzC,WAAoB;CAEpB,YAAoB,OAAgB,gBAAwB;EAC1D,MAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EAC1E,MAAM,YAAY;EAClB,KAAK,OACH,iBAAiB,QACb,MAAM,OACN,GAAG,eAAe,EAAE,CAAC,YAAY,IAAI,eAAe,MAAM,CAAC,EAAE;EAEnE,IAAI,iBAAiB,OACnB,KAAK,QAAQ;CAEjB;;;;;;;;;CAUA,OAAO,KAAK,OAAgB,gBAA+B;EAGzD,KAAA,GAAA,qBAAA,gBAAA,CAAoB,KAAK,GACvB,OAAO;EAET,OAAO,IAAI,gBAAgB,OAAO,cAAc;CAClD;;;;;;CAOA,OAAO,WAAW,OAA0C;EAC1D,OACE,iBAAiB,SACjB,YAAY,SACZ,MAAM,cAAc;CAExB;AACF"}
|
|
1
|
+
{"version":3,"file":"errors.cjs","names":[],"sources":["../../src/agents/errors.ts"],"sourcesContent":["/* oxlint-disable no-instanceof/no-instanceof */\nimport type { ToolCall } from \"@langchain/core/messages/tool\";\nimport { isGraphBubbleUp } from \"@langchain/langgraph\";\n\nexport class MultipleToolsBoundError extends Error {\n constructor() {\n super(\n \"The provided LLM already has bound tools. \" +\n \"Please provide an LLM without bound tools to createAgent. \" +\n \"The agent will bind the tools provided in the 'tools' parameter.\"\n );\n }\n}\n\n/**\n * Raised when model returns multiple structured output tool calls when only one is expected.\n */\nexport class MultipleStructuredOutputsError extends Error {\n public readonly toolNames: string[];\n\n constructor(toolNames: string[]) {\n super(\n `The model has called multiple tools: ${toolNames.join(\n \", \"\n )} to return a structured output. ` +\n \"This is not supported. Please provide a single structured output.\"\n );\n this.toolNames = toolNames;\n }\n}\n\n/**\n * Raised when structured output tool call arguments fail to parse according to the schema.\n */\nexport class StructuredOutputParsingError extends Error {\n public readonly toolName: string;\n\n public readonly errors: string[];\n\n constructor(toolName: string, errors: string[]) {\n super(\n `Failed to parse structured output for tool '${toolName}':${errors\n .map((e) => `\\n - ${e}`)\n .join(\"\")}.`\n );\n this.toolName = toolName;\n this.errors = errors;\n }\n}\n\n/**\n * Raised when a tool call is throwing an error.\n */\nexport class ToolInvocationError extends Error {\n readonly \"~brand\" = \"ToolInvocationError\";\n\n public readonly toolCall: ToolCall;\n\n public readonly toolError: Error;\n\n constructor(toolError: unknown, toolCall: ToolCall) {\n const error =\n toolError instanceof Error ? toolError : new Error(String(toolError));\n const toolArgs = JSON.stringify(toolCall.args);\n super(\n `Error invoking tool '${toolCall.name}' with kwargs ${toolArgs} with error: ${error.stack}\\n Please fix the error and try again.`\n );\n\n this.toolCall = toolCall;\n this.toolError = error;\n }\n\n /**\n * Check if the error is a ToolInvocationError.\n * @param error - The error to check\n * @returns Whether the error is a ToolInvocationError\n */\n static isInstance(error: unknown): error is ToolInvocationError {\n return (\n error instanceof Error &&\n \"~brand\" in error &&\n error[\"~brand\"] === \"ToolInvocationError\"\n );\n }\n\n static [Symbol.hasInstance](obj: unknown) {\n return this.isInstance(obj);\n }\n}\n\n/**\n * Error thrown when a middleware fails.\n *\n * Use `MiddlewareError.wrap()` to create instances. The constructor is private\n * to ensure that GraphBubbleUp errors (like GraphInterrupt) are never wrapped.\n */\nexport class MiddlewareError extends Error {\n readonly \"~brand\" = \"MiddlewareError\";\n\n private constructor(error: unknown, middlewareName: string) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n super(errorMessage);\n this.name =\n error instanceof Error\n ? error.name\n : `${middlewareName[0].toUpperCase() + middlewareName.slice(1)}Error`;\n\n if (error instanceof Error) {\n this.cause = error;\n }\n }\n\n /**\n * Wrap an error in a MiddlewareError, unless it's a GraphBubbleUp error\n * (like GraphInterrupt) which should propagate unchanged.\n *\n * @param error - The error to wrap\n * @param middlewareName - The name of the middleware that threw the error\n * @returns The original error if it's a GraphBubbleUp, otherwise a new MiddlewareError\n */\n static wrap(error: unknown, middlewareName: string): Error {\n // Don't wrap GraphBubbleUp errors (GraphInterrupt, NodeInterrupt, etc.)\n // These are control flow mechanisms that need to bubble up unchanged\n if (isGraphBubbleUp(error)) {\n return error;\n }\n return new MiddlewareError(error, middlewareName);\n }\n\n /**\n * Check if the error is a MiddlewareError.\n * @param error - The error to check\n * @returns Whether the error is a MiddlewareError\n */\n static isInstance(error: unknown): error is MiddlewareError {\n return (\n error instanceof Error &&\n \"~brand\" in error &&\n error[\"~brand\"] === \"MiddlewareError\"\n );\n }\n\n static [Symbol.hasInstance](obj: unknown) {\n return this.isInstance(obj);\n }\n}\n"],"mappings":";;AAIA,IAAa,0BAAb,cAA6C,MAAM;CACjD,cAAc;EACZ,MACE,sKAGF;CACF;AACF;;;;AAKA,IAAa,iCAAb,cAAoD,MAAM;CACxD;CAEA,YAAY,WAAqB;EAC/B,MACE,wCAAwC,UAAU,KAChD,IACF,EAAE,kGAEJ;EACA,KAAK,YAAY;CACnB;AACF;;;;AAKA,IAAa,+BAAb,cAAkD,MAAM;CACtD;CAEA;CAEA,YAAY,UAAkB,QAAkB;EAC9C,MACE,+CAA+C,SAAS,IAAI,OACzD,KAAK,MAAM,SAAS,GAAG,CAAC,CACxB,KAAK,EAAE,EAAE,EACd;EACA,KAAK,WAAW;EAChB,KAAK,SAAS;CAChB;AACF;;;;AAKA,IAAa,sBAAb,cAAyC,MAAM;CAC7C,WAAoB;CAEpB;CAEA;CAEA,YAAY,WAAoB,UAAoB;EAClD,MAAM,QACJ,qBAAqB,QAAQ,YAAY,IAAI,MAAM,OAAO,SAAS,CAAC;EACtE,MAAM,WAAW,KAAK,UAAU,SAAS,IAAI;EAC7C,MACE,wBAAwB,SAAS,KAAK,gBAAgB,SAAS,eAAe,MAAM,MAAM,uCAC5F;EAEA,KAAK,WAAW;EAChB,KAAK,YAAY;CACnB;;;;;;CAOA,OAAO,WAAW,OAA8C;EAC9D,OACE,iBAAiB,SACjB,YAAY,SACZ,MAAM,cAAc;CAExB;CAEA,QAAQ,OAAO,aAAa,KAAc;EACxC,OAAO,KAAK,WAAW,GAAG;CAC5B;AACF;;;;;;;AAQA,IAAa,kBAAb,MAAa,wBAAwB,MAAM;CACzC,WAAoB;CAEpB,YAAoB,OAAgB,gBAAwB;EAC1D,MAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EAC1E,MAAM,YAAY;EAClB,KAAK,OACH,iBAAiB,QACb,MAAM,OACN,GAAG,eAAe,EAAE,CAAC,YAAY,IAAI,eAAe,MAAM,CAAC,EAAE;EAEnE,IAAI,iBAAiB,OACnB,KAAK,QAAQ;CAEjB;;;;;;;;;CAUA,OAAO,KAAK,OAAgB,gBAA+B;EAGzD,KAAA,GAAA,qBAAA,gBAAA,CAAoB,KAAK,GACvB,OAAO;EAET,OAAO,IAAI,gBAAgB,OAAO,cAAc;CAClD;;;;;;CAOA,OAAO,WAAW,OAA0C;EAC1D,OACE,iBAAiB,SACjB,YAAY,SACZ,MAAM,cAAc;CAExB;CAEA,QAAQ,OAAO,aAAa,KAAc;EACxC,OAAO,KAAK,WAAW,GAAG;CAC5B;AACF"}
|
package/dist/agents/errors.d.cts
CHANGED
|
@@ -32,6 +32,7 @@ declare class ToolInvocationError extends Error {
|
|
|
32
32
|
* @returns Whether the error is a ToolInvocationError
|
|
33
33
|
*/
|
|
34
34
|
static isInstance(error: unknown): error is ToolInvocationError;
|
|
35
|
+
static [Symbol.hasInstance](obj: unknown): obj is ToolInvocationError;
|
|
35
36
|
}
|
|
36
37
|
/**
|
|
37
38
|
* Error thrown when a middleware fails.
|
|
@@ -57,6 +58,7 @@ declare class MiddlewareError extends Error {
|
|
|
57
58
|
* @returns Whether the error is a MiddlewareError
|
|
58
59
|
*/
|
|
59
60
|
static isInstance(error: unknown): error is MiddlewareError;
|
|
61
|
+
static [Symbol.hasInstance](obj: unknown): obj is MiddlewareError;
|
|
60
62
|
}
|
|
61
63
|
//#endregion
|
|
62
64
|
export { MiddlewareError, MultipleStructuredOutputsError, MultipleToolsBoundError, StructuredOutputParsingError, ToolInvocationError };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.cts","names":[],"sources":["../../src/agents/errors.ts"],"mappings":";;cAIa,gCAAgC;EAC3C;;;;;cAYW,uCAAuC;WAClC;EAEhB,YAAY;;;;;cAcD,qCAAqC;WAChC;WAEA;EAEhB,YAAY,kBAAkB;;;;;cAcnB,4BAA4B;;WAGvB,UAAU;WAEV,WAAW;EAE3B,YAAY,oBAAoB,UAAU;;;;;;SAiBnC,WAAW,iBAAiB,SAAS;;;;;;;;
|
|
1
|
+
{"version":3,"file":"errors.d.cts","names":[],"sources":["../../src/agents/errors.ts"],"mappings":";;cAIa,gCAAgC;EAC3C;;;;;cAYW,uCAAuC;WAClC;EAEhB,YAAY;;;;;cAcD,qCAAqC;WAChC;WAEA;EAEhB,YAAY,kBAAkB;;;;;cAcnB,4BAA4B;;WAGvB,UAAU;WAEV,WAAW;EAE3B,YAAY,oBAAoB,UAAU;;;;;;SAiBnC,WAAW,iBAAiB,SAAS;UAQpC,OAAO,aAAa,eAAY,OAAA;;;;;;;;cAW7B,wBAAwB;;UAG5B;;;;;;;;;SAqBA,KAAK,gBAAgB,yBAAyB;;;;;;SAc9C,WAAW,iBAAiB,SAAS;UAQpC,OAAO,aAAa,eAAY,OAAA"}
|
package/dist/agents/errors.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ declare class ToolInvocationError extends Error {
|
|
|
32
32
|
* @returns Whether the error is a ToolInvocationError
|
|
33
33
|
*/
|
|
34
34
|
static isInstance(error: unknown): error is ToolInvocationError;
|
|
35
|
+
static [Symbol.hasInstance](obj: unknown): obj is ToolInvocationError;
|
|
35
36
|
}
|
|
36
37
|
/**
|
|
37
38
|
* Error thrown when a middleware fails.
|
|
@@ -57,6 +58,7 @@ declare class MiddlewareError extends Error {
|
|
|
57
58
|
* @returns Whether the error is a MiddlewareError
|
|
58
59
|
*/
|
|
59
60
|
static isInstance(error: unknown): error is MiddlewareError;
|
|
61
|
+
static [Symbol.hasInstance](obj: unknown): obj is MiddlewareError;
|
|
60
62
|
}
|
|
61
63
|
//#endregion
|
|
62
64
|
export { MiddlewareError, MultipleStructuredOutputsError, MultipleToolsBoundError, StructuredOutputParsingError, ToolInvocationError };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","names":[],"sources":["../../src/agents/errors.ts"],"mappings":";;cAIa,gCAAgC;EAC3C;;;;;cAYW,uCAAuC;WAClC;EAEhB,YAAY;;;;;cAcD,qCAAqC;WAChC;WAEA;EAEhB,YAAY,kBAAkB;;;;;cAcnB,4BAA4B;;WAGvB,UAAU;WAEV,WAAW;EAE3B,YAAY,oBAAoB,UAAU;;;;;;SAiBnC,WAAW,iBAAiB,SAAS;;;;;;;;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","names":[],"sources":["../../src/agents/errors.ts"],"mappings":";;cAIa,gCAAgC;EAC3C;;;;;cAYW,uCAAuC;WAClC;EAEhB,YAAY;;;;;cAcD,qCAAqC;WAChC;WAEA;EAEhB,YAAY,kBAAkB;;;;;cAcnB,4BAA4B;;WAGvB,UAAU;WAEV,WAAW;EAE3B,YAAY,oBAAoB,UAAU;;;;;;SAiBnC,WAAW,iBAAiB,SAAS;UAQpC,OAAO,aAAa,eAAY,OAAA;;;;;;;;cAW7B,wBAAwB;;UAG5B;;;;;;;;;SAqBA,KAAK,gBAAgB,yBAAyB;;;;;;SAc9C,WAAW,iBAAiB,SAAS;UAQpC,OAAO,aAAa,eAAY,OAAA"}
|
package/dist/agents/errors.js
CHANGED
|
@@ -49,6 +49,9 @@ var ToolInvocationError = class extends Error {
|
|
|
49
49
|
static isInstance(error) {
|
|
50
50
|
return error instanceof Error && "~brand" in error && error["~brand"] === "ToolInvocationError";
|
|
51
51
|
}
|
|
52
|
+
static [Symbol.hasInstance](obj) {
|
|
53
|
+
return this.isInstance(obj);
|
|
54
|
+
}
|
|
52
55
|
};
|
|
53
56
|
/**
|
|
54
57
|
* Error thrown when a middleware fails.
|
|
@@ -84,6 +87,9 @@ var MiddlewareError = class MiddlewareError extends Error {
|
|
|
84
87
|
static isInstance(error) {
|
|
85
88
|
return error instanceof Error && "~brand" in error && error["~brand"] === "MiddlewareError";
|
|
86
89
|
}
|
|
90
|
+
static [Symbol.hasInstance](obj) {
|
|
91
|
+
return this.isInstance(obj);
|
|
92
|
+
}
|
|
87
93
|
};
|
|
88
94
|
//#endregion
|
|
89
95
|
export { MiddlewareError, MultipleStructuredOutputsError, MultipleToolsBoundError, StructuredOutputParsingError, ToolInvocationError };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","names":[],"sources":["../../src/agents/errors.ts"],"sourcesContent":["/* oxlint-disable no-instanceof/no-instanceof */\nimport type { ToolCall } from \"@langchain/core/messages/tool\";\nimport { isGraphBubbleUp } from \"@langchain/langgraph\";\n\nexport class MultipleToolsBoundError extends Error {\n constructor() {\n super(\n \"The provided LLM already has bound tools. \" +\n \"Please provide an LLM without bound tools to createAgent. \" +\n \"The agent will bind the tools provided in the 'tools' parameter.\"\n );\n }\n}\n\n/**\n * Raised when model returns multiple structured output tool calls when only one is expected.\n */\nexport class MultipleStructuredOutputsError extends Error {\n public readonly toolNames: string[];\n\n constructor(toolNames: string[]) {\n super(\n `The model has called multiple tools: ${toolNames.join(\n \", \"\n )} to return a structured output. ` +\n \"This is not supported. Please provide a single structured output.\"\n );\n this.toolNames = toolNames;\n }\n}\n\n/**\n * Raised when structured output tool call arguments fail to parse according to the schema.\n */\nexport class StructuredOutputParsingError extends Error {\n public readonly toolName: string;\n\n public readonly errors: string[];\n\n constructor(toolName: string, errors: string[]) {\n super(\n `Failed to parse structured output for tool '${toolName}':${errors\n .map((e) => `\\n - ${e}`)\n .join(\"\")}.`\n );\n this.toolName = toolName;\n this.errors = errors;\n }\n}\n\n/**\n * Raised when a tool call is throwing an error.\n */\nexport class ToolInvocationError extends Error {\n readonly \"~brand\" = \"ToolInvocationError\";\n\n public readonly toolCall: ToolCall;\n\n public readonly toolError: Error;\n\n constructor(toolError: unknown, toolCall: ToolCall) {\n const error =\n toolError instanceof Error ? toolError : new Error(String(toolError));\n const toolArgs = JSON.stringify(toolCall.args);\n super(\n `Error invoking tool '${toolCall.name}' with kwargs ${toolArgs} with error: ${error.stack}\\n Please fix the error and try again.`\n );\n\n this.toolCall = toolCall;\n this.toolError = error;\n }\n\n /**\n * Check if the error is a ToolInvocationError.\n * @param error - The error to check\n * @returns Whether the error is a ToolInvocationError\n */\n static isInstance(error: unknown): error is ToolInvocationError {\n return (\n error instanceof Error &&\n \"~brand\" in error &&\n error[\"~brand\"] === \"ToolInvocationError\"\n );\n }\n}\n\n/**\n * Error thrown when a middleware fails.\n *\n * Use `MiddlewareError.wrap()` to create instances. The constructor is private\n * to ensure that GraphBubbleUp errors (like GraphInterrupt) are never wrapped.\n */\nexport class MiddlewareError extends Error {\n readonly \"~brand\" = \"MiddlewareError\";\n\n private constructor(error: unknown, middlewareName: string) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n super(errorMessage);\n this.name =\n error instanceof Error\n ? error.name\n : `${middlewareName[0].toUpperCase() + middlewareName.slice(1)}Error`;\n\n if (error instanceof Error) {\n this.cause = error;\n }\n }\n\n /**\n * Wrap an error in a MiddlewareError, unless it's a GraphBubbleUp error\n * (like GraphInterrupt) which should propagate unchanged.\n *\n * @param error - The error to wrap\n * @param middlewareName - The name of the middleware that threw the error\n * @returns The original error if it's a GraphBubbleUp, otherwise a new MiddlewareError\n */\n static wrap(error: unknown, middlewareName: string): Error {\n // Don't wrap GraphBubbleUp errors (GraphInterrupt, NodeInterrupt, etc.)\n // These are control flow mechanisms that need to bubble up unchanged\n if (isGraphBubbleUp(error)) {\n return error;\n }\n return new MiddlewareError(error, middlewareName);\n }\n\n /**\n * Check if the error is a MiddlewareError.\n * @param error - The error to check\n * @returns Whether the error is a MiddlewareError\n */\n static isInstance(error: unknown): error is MiddlewareError {\n return (\n error instanceof Error &&\n \"~brand\" in error &&\n error[\"~brand\"] === \"MiddlewareError\"\n );\n }\n}\n"],"mappings":";;AAIA,IAAa,0BAAb,cAA6C,MAAM;CACjD,cAAc;EACZ,MACE,sKAGF;CACF;AACF;;;;AAKA,IAAa,iCAAb,cAAoD,MAAM;CACxD;CAEA,YAAY,WAAqB;EAC/B,MACE,wCAAwC,UAAU,KAChD,IACF,EAAE,kGAEJ;EACA,KAAK,YAAY;CACnB;AACF;;;;AAKA,IAAa,+BAAb,cAAkD,MAAM;CACtD;CAEA;CAEA,YAAY,UAAkB,QAAkB;EAC9C,MACE,+CAA+C,SAAS,IAAI,OACzD,KAAK,MAAM,SAAS,GAAG,CAAC,CACxB,KAAK,EAAE,EAAE,EACd;EACA,KAAK,WAAW;EAChB,KAAK,SAAS;CAChB;AACF;;;;AAKA,IAAa,sBAAb,cAAyC,MAAM;CAC7C,WAAoB;CAEpB;CAEA;CAEA,YAAY,WAAoB,UAAoB;EAClD,MAAM,QACJ,qBAAqB,QAAQ,YAAY,IAAI,MAAM,OAAO,SAAS,CAAC;EACtE,MAAM,WAAW,KAAK,UAAU,SAAS,IAAI;EAC7C,MACE,wBAAwB,SAAS,KAAK,gBAAgB,SAAS,eAAe,MAAM,MAAM,uCAC5F;EAEA,KAAK,WAAW;EAChB,KAAK,YAAY;CACnB;;;;;;CAOA,OAAO,WAAW,OAA8C;EAC9D,OACE,iBAAiB,SACjB,YAAY,SACZ,MAAM,cAAc;CAExB;AACF;;;;;;;AAQA,IAAa,kBAAb,MAAa,wBAAwB,MAAM;CACzC,WAAoB;CAEpB,YAAoB,OAAgB,gBAAwB;EAC1D,MAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EAC1E,MAAM,YAAY;EAClB,KAAK,OACH,iBAAiB,QACb,MAAM,OACN,GAAG,eAAe,EAAE,CAAC,YAAY,IAAI,eAAe,MAAM,CAAC,EAAE;EAEnE,IAAI,iBAAiB,OACnB,KAAK,QAAQ;CAEjB;;;;;;;;;CAUA,OAAO,KAAK,OAAgB,gBAA+B;EAGzD,IAAI,gBAAgB,KAAK,GACvB,OAAO;EAET,OAAO,IAAI,gBAAgB,OAAO,cAAc;CAClD;;;;;;CAOA,OAAO,WAAW,OAA0C;EAC1D,OACE,iBAAiB,SACjB,YAAY,SACZ,MAAM,cAAc;CAExB;AACF"}
|
|
1
|
+
{"version":3,"file":"errors.js","names":[],"sources":["../../src/agents/errors.ts"],"sourcesContent":["/* oxlint-disable no-instanceof/no-instanceof */\nimport type { ToolCall } from \"@langchain/core/messages/tool\";\nimport { isGraphBubbleUp } from \"@langchain/langgraph\";\n\nexport class MultipleToolsBoundError extends Error {\n constructor() {\n super(\n \"The provided LLM already has bound tools. \" +\n \"Please provide an LLM without bound tools to createAgent. \" +\n \"The agent will bind the tools provided in the 'tools' parameter.\"\n );\n }\n}\n\n/**\n * Raised when model returns multiple structured output tool calls when only one is expected.\n */\nexport class MultipleStructuredOutputsError extends Error {\n public readonly toolNames: string[];\n\n constructor(toolNames: string[]) {\n super(\n `The model has called multiple tools: ${toolNames.join(\n \", \"\n )} to return a structured output. ` +\n \"This is not supported. Please provide a single structured output.\"\n );\n this.toolNames = toolNames;\n }\n}\n\n/**\n * Raised when structured output tool call arguments fail to parse according to the schema.\n */\nexport class StructuredOutputParsingError extends Error {\n public readonly toolName: string;\n\n public readonly errors: string[];\n\n constructor(toolName: string, errors: string[]) {\n super(\n `Failed to parse structured output for tool '${toolName}':${errors\n .map((e) => `\\n - ${e}`)\n .join(\"\")}.`\n );\n this.toolName = toolName;\n this.errors = errors;\n }\n}\n\n/**\n * Raised when a tool call is throwing an error.\n */\nexport class ToolInvocationError extends Error {\n readonly \"~brand\" = \"ToolInvocationError\";\n\n public readonly toolCall: ToolCall;\n\n public readonly toolError: Error;\n\n constructor(toolError: unknown, toolCall: ToolCall) {\n const error =\n toolError instanceof Error ? toolError : new Error(String(toolError));\n const toolArgs = JSON.stringify(toolCall.args);\n super(\n `Error invoking tool '${toolCall.name}' with kwargs ${toolArgs} with error: ${error.stack}\\n Please fix the error and try again.`\n );\n\n this.toolCall = toolCall;\n this.toolError = error;\n }\n\n /**\n * Check if the error is a ToolInvocationError.\n * @param error - The error to check\n * @returns Whether the error is a ToolInvocationError\n */\n static isInstance(error: unknown): error is ToolInvocationError {\n return (\n error instanceof Error &&\n \"~brand\" in error &&\n error[\"~brand\"] === \"ToolInvocationError\"\n );\n }\n\n static [Symbol.hasInstance](obj: unknown) {\n return this.isInstance(obj);\n }\n}\n\n/**\n * Error thrown when a middleware fails.\n *\n * Use `MiddlewareError.wrap()` to create instances. The constructor is private\n * to ensure that GraphBubbleUp errors (like GraphInterrupt) are never wrapped.\n */\nexport class MiddlewareError extends Error {\n readonly \"~brand\" = \"MiddlewareError\";\n\n private constructor(error: unknown, middlewareName: string) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n super(errorMessage);\n this.name =\n error instanceof Error\n ? error.name\n : `${middlewareName[0].toUpperCase() + middlewareName.slice(1)}Error`;\n\n if (error instanceof Error) {\n this.cause = error;\n }\n }\n\n /**\n * Wrap an error in a MiddlewareError, unless it's a GraphBubbleUp error\n * (like GraphInterrupt) which should propagate unchanged.\n *\n * @param error - The error to wrap\n * @param middlewareName - The name of the middleware that threw the error\n * @returns The original error if it's a GraphBubbleUp, otherwise a new MiddlewareError\n */\n static wrap(error: unknown, middlewareName: string): Error {\n // Don't wrap GraphBubbleUp errors (GraphInterrupt, NodeInterrupt, etc.)\n // These are control flow mechanisms that need to bubble up unchanged\n if (isGraphBubbleUp(error)) {\n return error;\n }\n return new MiddlewareError(error, middlewareName);\n }\n\n /**\n * Check if the error is a MiddlewareError.\n * @param error - The error to check\n * @returns Whether the error is a MiddlewareError\n */\n static isInstance(error: unknown): error is MiddlewareError {\n return (\n error instanceof Error &&\n \"~brand\" in error &&\n error[\"~brand\"] === \"MiddlewareError\"\n );\n }\n\n static [Symbol.hasInstance](obj: unknown) {\n return this.isInstance(obj);\n }\n}\n"],"mappings":";;AAIA,IAAa,0BAAb,cAA6C,MAAM;CACjD,cAAc;EACZ,MACE,sKAGF;CACF;AACF;;;;AAKA,IAAa,iCAAb,cAAoD,MAAM;CACxD;CAEA,YAAY,WAAqB;EAC/B,MACE,wCAAwC,UAAU,KAChD,IACF,EAAE,kGAEJ;EACA,KAAK,YAAY;CACnB;AACF;;;;AAKA,IAAa,+BAAb,cAAkD,MAAM;CACtD;CAEA;CAEA,YAAY,UAAkB,QAAkB;EAC9C,MACE,+CAA+C,SAAS,IAAI,OACzD,KAAK,MAAM,SAAS,GAAG,CAAC,CACxB,KAAK,EAAE,EAAE,EACd;EACA,KAAK,WAAW;EAChB,KAAK,SAAS;CAChB;AACF;;;;AAKA,IAAa,sBAAb,cAAyC,MAAM;CAC7C,WAAoB;CAEpB;CAEA;CAEA,YAAY,WAAoB,UAAoB;EAClD,MAAM,QACJ,qBAAqB,QAAQ,YAAY,IAAI,MAAM,OAAO,SAAS,CAAC;EACtE,MAAM,WAAW,KAAK,UAAU,SAAS,IAAI;EAC7C,MACE,wBAAwB,SAAS,KAAK,gBAAgB,SAAS,eAAe,MAAM,MAAM,uCAC5F;EAEA,KAAK,WAAW;EAChB,KAAK,YAAY;CACnB;;;;;;CAOA,OAAO,WAAW,OAA8C;EAC9D,OACE,iBAAiB,SACjB,YAAY,SACZ,MAAM,cAAc;CAExB;CAEA,QAAQ,OAAO,aAAa,KAAc;EACxC,OAAO,KAAK,WAAW,GAAG;CAC5B;AACF;;;;;;;AAQA,IAAa,kBAAb,MAAa,wBAAwB,MAAM;CACzC,WAAoB;CAEpB,YAAoB,OAAgB,gBAAwB;EAC1D,MAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EAC1E,MAAM,YAAY;EAClB,KAAK,OACH,iBAAiB,QACb,MAAM,OACN,GAAG,eAAe,EAAE,CAAC,YAAY,IAAI,eAAe,MAAM,CAAC,EAAE;EAEnE,IAAI,iBAAiB,OACnB,KAAK,QAAQ;CAEjB;;;;;;;;;CAUA,OAAO,KAAK,OAAgB,gBAA+B;EAGzD,IAAI,gBAAgB,KAAK,GACvB,OAAO;EAET,OAAO,IAAI,gBAAgB,OAAO,cAAc;CAClD;;;;;;CAOA,OAAO,WAAW,OAA0C;EAC1D,OACE,iBAAiB,SACjB,YAAY,SACZ,MAAM,cAAc;CAExB;CAEA,QAAQ,OAAO,aAAa,KAAc;EACxC,OAAO,KAAK,WAAW,GAAG;CAC5B;AACF"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
let zod_v3 = require("zod/v3");
|
|
2
2
|
//#region src/agents/middleware/constants.ts
|
|
3
|
+
/** LangGraph's messages handlers skip runs tagged with this, keeping middleware-internal model calls out of the messages stream. */
|
|
4
|
+
const INTERNAL_CALL_TAG = "nostream";
|
|
3
5
|
const RetrySchema = zod_v3.z.object({
|
|
4
6
|
/**
|
|
5
7
|
* Maximum number of retry attempts after the initial call.
|
|
@@ -34,6 +36,7 @@ const RetrySchema = zod_v3.z.object({
|
|
|
34
36
|
jitter: zod_v3.z.boolean().default(true)
|
|
35
37
|
});
|
|
36
38
|
//#endregion
|
|
39
|
+
exports.INTERNAL_CALL_TAG = INTERNAL_CALL_TAG;
|
|
37
40
|
exports.RetrySchema = RetrySchema;
|
|
38
41
|
|
|
39
42
|
//# sourceMappingURL=constants.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.cjs","names":["z"],"sources":["../../../src/agents/middleware/constants.ts"],"sourcesContent":["import { z } from \"zod/v3\";\n\nexport const RetrySchema = z.object({\n /**\n * Maximum number of retry attempts after the initial call.\n * Default is 2 retries (3 total attempts). Must be >= 0.\n */\n maxRetries: z.number().min(0).default(2),\n\n /**\n * Either an array of error constructors to retry on, or a function\n * that takes an error and returns `true` if it should be retried.\n * Default is to retry on all errors.\n */\n retryOn: z\n .union([\n z.function().args(z.instanceof(Error)).returns(z.boolean()),\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n z.array(z.custom<new (...args: any[]) => Error>()),\n ])\n .default(() => () => true),\n\n /**\n * Multiplier for exponential backoff. Each retry waits\n * `initialDelayMs * (backoffFactor ** retryNumber)` milliseconds.\n * Set to 0.0 for constant delay. Default is 2.0.\n */\n backoffFactor: z.number().min(0).default(2.0),\n\n /**\n * Initial delay in milliseconds before first retry. Default is 1000 (1 second).\n */\n initialDelayMs: z.number().min(0).default(1000),\n\n /**\n * Maximum delay in milliseconds between retries. Caps exponential\n * backoff growth. Default is 60000 (60 seconds).\n */\n maxDelayMs: z.number().min(0).default(60000),\n\n /**\n * Whether to add random jitter (±25%) to delay to avoid thundering herd.\n * Default is `true`.\n */\n jitter: z.boolean().default(true),\n});\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"constants.cjs","names":["z"],"sources":["../../../src/agents/middleware/constants.ts"],"sourcesContent":["import { z } from \"zod/v3\";\n\n/** LangGraph's messages handlers skip runs tagged with this, keeping middleware-internal model calls out of the messages stream. */\nexport const INTERNAL_CALL_TAG = \"nostream\";\n\nexport const RetrySchema = z.object({\n /**\n * Maximum number of retry attempts after the initial call.\n * Default is 2 retries (3 total attempts). Must be >= 0.\n */\n maxRetries: z.number().min(0).default(2),\n\n /**\n * Either an array of error constructors to retry on, or a function\n * that takes an error and returns `true` if it should be retried.\n * Default is to retry on all errors.\n */\n retryOn: z\n .union([\n z.function().args(z.instanceof(Error)).returns(z.boolean()),\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n z.array(z.custom<new (...args: any[]) => Error>()),\n ])\n .default(() => () => true),\n\n /**\n * Multiplier for exponential backoff. Each retry waits\n * `initialDelayMs * (backoffFactor ** retryNumber)` milliseconds.\n * Set to 0.0 for constant delay. Default is 2.0.\n */\n backoffFactor: z.number().min(0).default(2.0),\n\n /**\n * Initial delay in milliseconds before first retry. Default is 1000 (1 second).\n */\n initialDelayMs: z.number().min(0).default(1000),\n\n /**\n * Maximum delay in milliseconds between retries. Caps exponential\n * backoff growth. Default is 60000 (60 seconds).\n */\n maxDelayMs: z.number().min(0).default(60000),\n\n /**\n * Whether to add random jitter (±25%) to delay to avoid thundering herd.\n * Default is `true`.\n */\n jitter: z.boolean().default(true),\n});\n"],"mappings":";;;AAGA,MAAa,oBAAoB;AAEjC,MAAa,cAAcA,OAAAA,EAAE,OAAO;;;;;CAKlC,YAAYA,OAAAA,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC;;;;;;CAOvC,SAASA,OAAAA,EACN,MAAM,CACLA,OAAAA,EAAE,SAAS,CAAC,CAAC,KAAKA,OAAAA,EAAE,WAAW,KAAK,CAAC,CAAC,CAAC,QAAQA,OAAAA,EAAE,QAAQ,CAAC,GAE1DA,OAAAA,EAAE,MAAMA,OAAAA,EAAE,OAAsC,CAAC,CACnD,CAAC,CAAC,CACD,oBAAoB,IAAI;;;;;;CAO3B,eAAeA,OAAAA,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAG;;;;CAK5C,gBAAgBA,OAAAA,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,GAAI;;;;;CAM9C,YAAYA,OAAAA,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,GAAK;;;;;CAM3C,QAAQA,OAAAA,EAAE,QAAQ,CAAC,CAAC,QAAQ,IAAI;AAClC,CAAC"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from "zod/v3";
|
|
2
2
|
//#region src/agents/middleware/constants.ts
|
|
3
|
+
/** LangGraph's messages handlers skip runs tagged with this, keeping middleware-internal model calls out of the messages stream. */
|
|
4
|
+
const INTERNAL_CALL_TAG = "nostream";
|
|
3
5
|
const RetrySchema = z.object({
|
|
4
6
|
/**
|
|
5
7
|
* Maximum number of retry attempts after the initial call.
|
|
@@ -34,6 +36,6 @@ const RetrySchema = z.object({
|
|
|
34
36
|
jitter: z.boolean().default(true)
|
|
35
37
|
});
|
|
36
38
|
//#endregion
|
|
37
|
-
export { RetrySchema };
|
|
39
|
+
export { INTERNAL_CALL_TAG, RetrySchema };
|
|
38
40
|
|
|
39
41
|
//# sourceMappingURL=constants.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","names":[],"sources":["../../../src/agents/middleware/constants.ts"],"sourcesContent":["import { z } from \"zod/v3\";\n\nexport const RetrySchema = z.object({\n /**\n * Maximum number of retry attempts after the initial call.\n * Default is 2 retries (3 total attempts). Must be >= 0.\n */\n maxRetries: z.number().min(0).default(2),\n\n /**\n * Either an array of error constructors to retry on, or a function\n * that takes an error and returns `true` if it should be retried.\n * Default is to retry on all errors.\n */\n retryOn: z\n .union([\n z.function().args(z.instanceof(Error)).returns(z.boolean()),\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n z.array(z.custom<new (...args: any[]) => Error>()),\n ])\n .default(() => () => true),\n\n /**\n * Multiplier for exponential backoff. Each retry waits\n * `initialDelayMs * (backoffFactor ** retryNumber)` milliseconds.\n * Set to 0.0 for constant delay. Default is 2.0.\n */\n backoffFactor: z.number().min(0).default(2.0),\n\n /**\n * Initial delay in milliseconds before first retry. Default is 1000 (1 second).\n */\n initialDelayMs: z.number().min(0).default(1000),\n\n /**\n * Maximum delay in milliseconds between retries. Caps exponential\n * backoff growth. Default is 60000 (60 seconds).\n */\n maxDelayMs: z.number().min(0).default(60000),\n\n /**\n * Whether to add random jitter (±25%) to delay to avoid thundering herd.\n * Default is `true`.\n */\n jitter: z.boolean().default(true),\n});\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"constants.js","names":[],"sources":["../../../src/agents/middleware/constants.ts"],"sourcesContent":["import { z } from \"zod/v3\";\n\n/** LangGraph's messages handlers skip runs tagged with this, keeping middleware-internal model calls out of the messages stream. */\nexport const INTERNAL_CALL_TAG = \"nostream\";\n\nexport const RetrySchema = z.object({\n /**\n * Maximum number of retry attempts after the initial call.\n * Default is 2 retries (3 total attempts). Must be >= 0.\n */\n maxRetries: z.number().min(0).default(2),\n\n /**\n * Either an array of error constructors to retry on, or a function\n * that takes an error and returns `true` if it should be retried.\n * Default is to retry on all errors.\n */\n retryOn: z\n .union([\n z.function().args(z.instanceof(Error)).returns(z.boolean()),\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n z.array(z.custom<new (...args: any[]) => Error>()),\n ])\n .default(() => () => true),\n\n /**\n * Multiplier for exponential backoff. Each retry waits\n * `initialDelayMs * (backoffFactor ** retryNumber)` milliseconds.\n * Set to 0.0 for constant delay. Default is 2.0.\n */\n backoffFactor: z.number().min(0).default(2.0),\n\n /**\n * Initial delay in milliseconds before first retry. Default is 1000 (1 second).\n */\n initialDelayMs: z.number().min(0).default(1000),\n\n /**\n * Maximum delay in milliseconds between retries. Caps exponential\n * backoff growth. Default is 60000 (60 seconds).\n */\n maxDelayMs: z.number().min(0).default(60000),\n\n /**\n * Whether to add random jitter (±25%) to delay to avoid thundering herd.\n * Default is `true`.\n */\n jitter: z.boolean().default(true),\n});\n"],"mappings":";;;AAGA,MAAa,oBAAoB;AAEjC,MAAa,cAAc,EAAE,OAAO;;;;;CAKlC,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC;;;;;;CAOvC,SAAS,EACN,MAAM,CACL,EAAE,SAAS,CAAC,CAAC,KAAK,EAAE,WAAW,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAE1D,EAAE,MAAM,EAAE,OAAsC,CAAC,CACnD,CAAC,CAAC,CACD,oBAAoB,IAAI;;;;;;CAO3B,eAAe,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAG;;;;CAK5C,gBAAgB,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,GAAI;;;;;CAM9C,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,GAAK;;;;;CAM3C,QAAQ,EAAE,QAAQ,CAAC,CAAC,QAAQ,IAAI;AAClC,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const require_chat_models_universal = require("../../chat_models/universal.cjs");
|
|
2
2
|
const require_middleware = require("../middleware.cjs");
|
|
3
|
+
const require_constants = require("./constants.cjs");
|
|
3
4
|
let _langchain_core_messages = require("@langchain/core/messages");
|
|
4
5
|
let _langchain_core_runnables = require("@langchain/core/runnables");
|
|
5
6
|
let zod_v3 = require("zod/v3");
|
|
@@ -90,6 +91,7 @@ function llmToolSelectorMiddleware(options) {
|
|
|
90
91
|
const structuredModel = await selectionRequest.model.withStructuredOutput?.(toolSelectionSchema);
|
|
91
92
|
const config = (0, _langchain_core_runnables.mergeConfigs)((0, _langchain_core_runnables.pickRunnableConfigKeys)(request.runtime) ?? {}, {
|
|
92
93
|
metadata: { lc_source: "llmToolSelector" },
|
|
94
|
+
tags: [require_constants.INTERNAL_CALL_TAG],
|
|
93
95
|
callbacks: []
|
|
94
96
|
});
|
|
95
97
|
const response = await structuredModel?.invoke([{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llmToolSelector.cjs","names":["z","BaseLanguageModel","createMiddleware","HumanMessage","initChatModel"],"sources":["../../../src/agents/middleware/llmToolSelector.ts"],"sourcesContent":["import { z } from \"zod/v3\";\nimport { BaseLanguageModel } from \"@langchain/core/language_models/base\";\nimport type { InferInteropZodInput } from \"@langchain/core/utils/types\";\nimport { HumanMessage } from \"@langchain/core/messages\";\nimport type { StructuredToolInterface } from \"@langchain/core/tools\";\n\nimport { createMiddleware } from \"../middleware.js\";\nimport { initChatModel } from \"../../chat_models/universal.js\";\nimport type { Runtime } from \"../runtime.js\";\nimport type { ModelRequest } from \"../nodes/types.js\";\nimport {\n mergeConfigs,\n pickRunnableConfigKeys,\n type RunnableConfig,\n} from \"@langchain/core/runnables\";\n\nconst DEFAULT_SYSTEM_PROMPT =\n \"Your goal is to select the most relevant tools for answering the user's query.\";\n\n/**\n * Prepared inputs for tool selection.\n */\ninterface SelectionRequest {\n availableTools: StructuredToolInterface[];\n systemMessage: string;\n lastUserMessage: HumanMessage;\n model: BaseLanguageModel;\n validToolNames: string[];\n}\n\n/**\n * Create a structured output schema for tool selection.\n *\n * @param tools - Available tools to include in the schema.\n * @returns Zod schema where each tool name is a literal with its description.\n */\nfunction createToolSelectionResponse(tools: StructuredToolInterface[]) {\n if (!tools || tools.length === 0) {\n throw new Error(\"Invalid usage: tools must be non-empty\");\n }\n\n // Create a union of literals for each tool name\n const toolLiterals = tools.map((tool) => z.literal(tool.name));\n const toolEnum = z.union(\n toolLiterals as [\n z.ZodLiteral<string>,\n z.ZodLiteral<string>,\n ...z.ZodLiteral<string>[],\n ]\n );\n\n return z.object({\n tools: z\n .array(toolEnum)\n .describe(\"Tools to use. Place the most relevant tools first.\"),\n });\n}\n\n/**\n * Options for configuring the LLM Tool Selector middleware.\n */\nexport const LLMToolSelectorOptionsSchema = z.object({\n /**\n * The language model to use for tool selection (default: the provided model from the agent options).\n */\n model: z.string().or(z.instanceof(BaseLanguageModel)).optional(),\n /**\n * System prompt for the tool selection model.\n */\n systemPrompt: z.string().optional(),\n /**\n * Maximum number of tools to select. If the model selects more,\n * only the first maxTools will be used. No limit if not specified.\n */\n maxTools: z.number().optional(),\n /**\n * Tool names to always include regardless of selection.\n * These do not count against the maxTools limit.\n */\n alwaysInclude: z.array(z.string()).optional(),\n});\nexport type LLMToolSelectorConfig = InferInteropZodInput<\n typeof LLMToolSelectorOptionsSchema\n>;\n\n/**\n * Middleware for selecting tools using an LLM-based strategy.\n *\n * When an agent has many tools available, this middleware filters them down\n * to only the most relevant ones for the user's query. This reduces token usage\n * and helps the main model focus on the right tools.\n *\n * @param options - Configuration options for the middleware\n * @param options.model - The language model to use for tool selection (default: the provided model from the agent options).\n * @param options.systemPrompt - Instructions for the selection model.\n * @param options.maxTools - Maximum number of tools to select. If the model selects more,\n * only the first maxTools will be used. No limit if not specified.\n * @param options.alwaysInclude - Tool names to always include regardless of selection.\n * These do not count against the maxTools limit.\n *\n * @example\n * Limit to 3 tools:\n * ```ts\n * import { llmToolSelectorMiddleware } from \"langchain/agents/middleware\";\n *\n * const middleware = llmToolSelectorMiddleware({ maxTools: 3 });\n *\n * const agent = createAgent({\n * model: \"openai:gpt-4o\",\n * tools: [tool1, tool2, tool3, tool4, tool5],\n * middleware: [middleware],\n * });\n * ```\n *\n * @example\n * Use a smaller model for selection:\n * ```ts\n * const middleware = llmToolSelectorMiddleware({\n * model: \"openai:gpt-4o-mini\",\n * maxTools: 2\n * });\n * ```\n */\nexport function llmToolSelectorMiddleware(options: LLMToolSelectorConfig) {\n return createMiddleware({\n name: \"LLMToolSelector\",\n contextSchema: LLMToolSelectorOptionsSchema,\n async wrapModelCall(request, handler) {\n const selectionRequest = await prepareSelectionRequest(\n request,\n options,\n request.runtime\n );\n if (!selectionRequest) {\n return handler(request);\n }\n\n // Create dynamic response model with union of literal tool names\n const toolSelectionSchema = createToolSelectionResponse(\n selectionRequest.availableTools\n );\n const structuredModel =\n await selectionRequest.model.withStructuredOutput?.(\n toolSelectionSchema\n );\n\n const baseConfig: RunnableConfig =\n pickRunnableConfigKeys(request.runtime) ?? {};\n const config = mergeConfigs(baseConfig, {\n metadata: { lc_source: \"llmToolSelector\" },\n callbacks: [],\n });\n\n const response = await structuredModel?.invoke(\n [\n { role: \"system\", content: selectionRequest.systemMessage },\n selectionRequest.lastUserMessage,\n ],\n config\n );\n\n // Response should be an object with a tools array\n if (!response || typeof response !== \"object\" || !(\"tools\" in response)) {\n throw new Error(\n `Expected object response with tools array, got ${typeof response}`\n );\n }\n\n return handler(\n processSelectionResponse(\n response as { tools: string[] },\n selectionRequest.availableTools,\n selectionRequest.validToolNames,\n request,\n options\n )\n );\n },\n });\n}\n\n/**\n * Prepare inputs for tool selection.\n *\n * @param request - The model request to process.\n * @param options - Configuration options.\n * @param runtime - Runtime context.\n * @returns SelectionRequest with prepared inputs, or null if no selection is needed.\n */\nasync function prepareSelectionRequest<\n TState extends Record<string, unknown> = Record<string, unknown>,\n TContext = unknown,\n>(\n request: ModelRequest<TState, TContext>,\n options: LLMToolSelectorConfig,\n runtime: Runtime<LLMToolSelectorConfig>\n): Promise<SelectionRequest | undefined> {\n const model = runtime.context.model ?? options.model;\n const maxTools = runtime.context.maxTools ?? options.maxTools;\n const alwaysInclude =\n runtime.context.alwaysInclude ?? options.alwaysInclude ?? [];\n const systemPrompt =\n runtime.context.systemPrompt ??\n options.systemPrompt ??\n DEFAULT_SYSTEM_PROMPT;\n\n /**\n * If no tools available, return null\n */\n if (!request.tools || request.tools.length === 0) {\n return undefined;\n }\n\n /**\n * Filter to only StructuredToolInterface instances (exclude provider-specific tool dicts)\n */\n const baseTools = request.tools.filter(\n (tool): tool is StructuredToolInterface =>\n typeof tool === \"object\" &&\n \"name\" in tool &&\n \"description\" in tool &&\n typeof tool.name === \"string\"\n );\n\n /**\n * Validate that alwaysInclude tools exist\n */\n if (alwaysInclude.length > 0) {\n const availableToolNames = new Set(baseTools.map((tool) => tool.name));\n const missingTools = alwaysInclude.filter(\n (name) => !availableToolNames.has(name)\n );\n if (missingTools.length > 0) {\n throw new Error(\n `Tools in alwaysInclude not found in request: ${missingTools.join(\n \", \"\n )}. ` +\n `Available tools: ${Array.from(availableToolNames).sort().join(\", \")}`\n );\n }\n }\n\n /**\n * Separate tools that are always included from those available for selection\n */\n const availableTools = baseTools.filter(\n (tool) => !alwaysInclude.includes(tool.name)\n );\n\n /**\n * If no tools available for selection, return null\n */\n if (availableTools.length === 0) {\n return undefined;\n }\n\n let systemMessage = systemPrompt;\n /**\n * If there's a maxTools limit, append instructions to the system prompt\n */\n if (maxTools !== undefined) {\n systemMessage +=\n `\\nIMPORTANT: List the tool names in order of relevance, ` +\n `with the most relevant first. ` +\n `If you exceed the maximum number of tools, ` +\n `only the first ${maxTools} will be used.`;\n }\n\n /**\n * Get the last user message from the conversation history\n */\n let lastUserMessage: HumanMessage | undefined;\n for (const message of request.messages) {\n if (HumanMessage.isInstance(message)) {\n lastUserMessage = message;\n }\n }\n\n if (!lastUserMessage) {\n throw new Error(\"No user message found in request messages\");\n }\n\n const modelInstance = !model\n ? (request.model as BaseLanguageModel)\n : typeof model === \"string\"\n ? await initChatModel(model)\n : model;\n\n const validToolNames = availableTools.map((tool) => tool.name);\n\n return {\n availableTools,\n systemMessage,\n lastUserMessage,\n model: modelInstance,\n validToolNames,\n };\n}\n\n/**\n * Process the selection response and return filtered ModelRequest.\n *\n * @param response - The structured output response from the model.\n * @param availableTools - Tools available for selection.\n * @param validToolNames - Valid tool names that can be selected.\n * @param request - Original model request.\n * @param options - Configuration options.\n * @returns Modified ModelRequest with filtered tools.\n */\nfunction processSelectionResponse<\n TState extends Record<string, unknown> = Record<string, unknown>,\n TContext = unknown,\n>(\n response: { tools: string[] },\n availableTools: StructuredToolInterface[],\n validToolNames: string[],\n request: ModelRequest<TState, TContext>,\n options: LLMToolSelectorConfig\n): ModelRequest<TState, TContext> {\n const maxTools = options.maxTools;\n const alwaysInclude = options.alwaysInclude ?? [];\n\n const selectedToolNames: string[] = [];\n const invalidToolSelections: string[] = [];\n\n for (const toolName of response.tools) {\n if (!validToolNames.includes(toolName)) {\n invalidToolSelections.push(toolName);\n continue;\n }\n\n /**\n * Only add if not already selected and within maxTools limit\n */\n if (\n !selectedToolNames.includes(toolName) &&\n (maxTools === undefined || selectedToolNames.length < maxTools)\n ) {\n selectedToolNames.push(toolName);\n }\n }\n\n if (invalidToolSelections.length > 0) {\n throw new Error(\n `Model selected invalid tools: ${invalidToolSelections.join(\", \")}`\n );\n }\n\n /**\n * Filter tools based on selection\n */\n const selectedTools = availableTools.filter((tool) =>\n selectedToolNames.includes(tool.name)\n );\n\n /**\n * Append always-included tools\n */\n const alwaysIncludedTools = (request.tools ?? []).filter(\n (tool): tool is StructuredToolInterface =>\n typeof tool === \"object\" &&\n \"name\" in tool &&\n typeof tool.name === \"string\" &&\n alwaysInclude.includes(tool.name)\n );\n selectedTools.push(...alwaysIncludedTools);\n\n /**\n * Also preserve any provider-specific tool dicts from the original request\n */\n const providerTools = (request.tools ?? []).filter(\n (tool) =>\n !(\n typeof tool === \"object\" &&\n \"name\" in tool &&\n \"description\" in tool &&\n typeof tool.name === \"string\"\n )\n );\n\n return {\n ...request,\n tools: [...selectedTools, ...providerTools],\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"llmToolSelector.cjs","names":["z","BaseLanguageModel","createMiddleware","INTERNAL_CALL_TAG","HumanMessage","initChatModel"],"sources":["../../../src/agents/middleware/llmToolSelector.ts"],"sourcesContent":["import { z } from \"zod/v3\";\nimport { BaseLanguageModel } from \"@langchain/core/language_models/base\";\nimport type { InferInteropZodInput } from \"@langchain/core/utils/types\";\nimport { HumanMessage } from \"@langchain/core/messages\";\nimport type { StructuredToolInterface } from \"@langchain/core/tools\";\n\nimport { createMiddleware } from \"../middleware.js\";\nimport { INTERNAL_CALL_TAG } from \"./constants.js\";\nimport { initChatModel } from \"../../chat_models/universal.js\";\nimport type { Runtime } from \"../runtime.js\";\nimport type { ModelRequest } from \"../nodes/types.js\";\nimport {\n mergeConfigs,\n pickRunnableConfigKeys,\n type RunnableConfig,\n} from \"@langchain/core/runnables\";\n\nconst DEFAULT_SYSTEM_PROMPT =\n \"Your goal is to select the most relevant tools for answering the user's query.\";\n\n/**\n * Prepared inputs for tool selection.\n */\ninterface SelectionRequest {\n availableTools: StructuredToolInterface[];\n systemMessage: string;\n lastUserMessage: HumanMessage;\n model: BaseLanguageModel;\n validToolNames: string[];\n}\n\n/**\n * Create a structured output schema for tool selection.\n *\n * @param tools - Available tools to include in the schema.\n * @returns Zod schema where each tool name is a literal with its description.\n */\nfunction createToolSelectionResponse(tools: StructuredToolInterface[]) {\n if (!tools || tools.length === 0) {\n throw new Error(\"Invalid usage: tools must be non-empty\");\n }\n\n // Create a union of literals for each tool name\n const toolLiterals = tools.map((tool) => z.literal(tool.name));\n const toolEnum = z.union(\n toolLiterals as [\n z.ZodLiteral<string>,\n z.ZodLiteral<string>,\n ...z.ZodLiteral<string>[],\n ]\n );\n\n return z.object({\n tools: z\n .array(toolEnum)\n .describe(\"Tools to use. Place the most relevant tools first.\"),\n });\n}\n\n/**\n * Options for configuring the LLM Tool Selector middleware.\n */\nexport const LLMToolSelectorOptionsSchema = z.object({\n /**\n * The language model to use for tool selection (default: the provided model from the agent options).\n */\n model: z.string().or(z.instanceof(BaseLanguageModel)).optional(),\n /**\n * System prompt for the tool selection model.\n */\n systemPrompt: z.string().optional(),\n /**\n * Maximum number of tools to select. If the model selects more,\n * only the first maxTools will be used. No limit if not specified.\n */\n maxTools: z.number().optional(),\n /**\n * Tool names to always include regardless of selection.\n * These do not count against the maxTools limit.\n */\n alwaysInclude: z.array(z.string()).optional(),\n});\nexport type LLMToolSelectorConfig = InferInteropZodInput<\n typeof LLMToolSelectorOptionsSchema\n>;\n\n/**\n * Middleware for selecting tools using an LLM-based strategy.\n *\n * When an agent has many tools available, this middleware filters them down\n * to only the most relevant ones for the user's query. This reduces token usage\n * and helps the main model focus on the right tools.\n *\n * @param options - Configuration options for the middleware\n * @param options.model - The language model to use for tool selection (default: the provided model from the agent options).\n * @param options.systemPrompt - Instructions for the selection model.\n * @param options.maxTools - Maximum number of tools to select. If the model selects more,\n * only the first maxTools will be used. No limit if not specified.\n * @param options.alwaysInclude - Tool names to always include regardless of selection.\n * These do not count against the maxTools limit.\n *\n * @example\n * Limit to 3 tools:\n * ```ts\n * import { llmToolSelectorMiddleware } from \"langchain/agents/middleware\";\n *\n * const middleware = llmToolSelectorMiddleware({ maxTools: 3 });\n *\n * const agent = createAgent({\n * model: \"openai:gpt-4o\",\n * tools: [tool1, tool2, tool3, tool4, tool5],\n * middleware: [middleware],\n * });\n * ```\n *\n * @example\n * Use a smaller model for selection:\n * ```ts\n * const middleware = llmToolSelectorMiddleware({\n * model: \"openai:gpt-4o-mini\",\n * maxTools: 2\n * });\n * ```\n */\nexport function llmToolSelectorMiddleware(options: LLMToolSelectorConfig) {\n return createMiddleware({\n name: \"LLMToolSelector\",\n contextSchema: LLMToolSelectorOptionsSchema,\n async wrapModelCall(request, handler) {\n const selectionRequest = await prepareSelectionRequest(\n request,\n options,\n request.runtime\n );\n if (!selectionRequest) {\n return handler(request);\n }\n\n // Create dynamic response model with union of literal tool names\n const toolSelectionSchema = createToolSelectionResponse(\n selectionRequest.availableTools\n );\n const structuredModel =\n await selectionRequest.model.withStructuredOutput?.(\n toolSelectionSchema\n );\n\n const baseConfig: RunnableConfig =\n pickRunnableConfigKeys(request.runtime) ?? {};\n const config = mergeConfigs(baseConfig, {\n metadata: { lc_source: \"llmToolSelector\" },\n tags: [INTERNAL_CALL_TAG],\n callbacks: [],\n });\n\n const response = await structuredModel?.invoke(\n [\n { role: \"system\", content: selectionRequest.systemMessage },\n selectionRequest.lastUserMessage,\n ],\n config\n );\n\n // Response should be an object with a tools array\n if (!response || typeof response !== \"object\" || !(\"tools\" in response)) {\n throw new Error(\n `Expected object response with tools array, got ${typeof response}`\n );\n }\n\n return handler(\n processSelectionResponse(\n response as { tools: string[] },\n selectionRequest.availableTools,\n selectionRequest.validToolNames,\n request,\n options\n )\n );\n },\n });\n}\n\n/**\n * Prepare inputs for tool selection.\n *\n * @param request - The model request to process.\n * @param options - Configuration options.\n * @param runtime - Runtime context.\n * @returns SelectionRequest with prepared inputs, or null if no selection is needed.\n */\nasync function prepareSelectionRequest<\n TState extends Record<string, unknown> = Record<string, unknown>,\n TContext = unknown,\n>(\n request: ModelRequest<TState, TContext>,\n options: LLMToolSelectorConfig,\n runtime: Runtime<LLMToolSelectorConfig>\n): Promise<SelectionRequest | undefined> {\n const model = runtime.context.model ?? options.model;\n const maxTools = runtime.context.maxTools ?? options.maxTools;\n const alwaysInclude =\n runtime.context.alwaysInclude ?? options.alwaysInclude ?? [];\n const systemPrompt =\n runtime.context.systemPrompt ??\n options.systemPrompt ??\n DEFAULT_SYSTEM_PROMPT;\n\n /**\n * If no tools available, return null\n */\n if (!request.tools || request.tools.length === 0) {\n return undefined;\n }\n\n /**\n * Filter to only StructuredToolInterface instances (exclude provider-specific tool dicts)\n */\n const baseTools = request.tools.filter(\n (tool): tool is StructuredToolInterface =>\n typeof tool === \"object\" &&\n \"name\" in tool &&\n \"description\" in tool &&\n typeof tool.name === \"string\"\n );\n\n /**\n * Validate that alwaysInclude tools exist\n */\n if (alwaysInclude.length > 0) {\n const availableToolNames = new Set(baseTools.map((tool) => tool.name));\n const missingTools = alwaysInclude.filter(\n (name) => !availableToolNames.has(name)\n );\n if (missingTools.length > 0) {\n throw new Error(\n `Tools in alwaysInclude not found in request: ${missingTools.join(\n \", \"\n )}. ` +\n `Available tools: ${Array.from(availableToolNames).sort().join(\", \")}`\n );\n }\n }\n\n /**\n * Separate tools that are always included from those available for selection\n */\n const availableTools = baseTools.filter(\n (tool) => !alwaysInclude.includes(tool.name)\n );\n\n /**\n * If no tools available for selection, return null\n */\n if (availableTools.length === 0) {\n return undefined;\n }\n\n let systemMessage = systemPrompt;\n /**\n * If there's a maxTools limit, append instructions to the system prompt\n */\n if (maxTools !== undefined) {\n systemMessage +=\n `\\nIMPORTANT: List the tool names in order of relevance, ` +\n `with the most relevant first. ` +\n `If you exceed the maximum number of tools, ` +\n `only the first ${maxTools} will be used.`;\n }\n\n /**\n * Get the last user message from the conversation history\n */\n let lastUserMessage: HumanMessage | undefined;\n for (const message of request.messages) {\n if (HumanMessage.isInstance(message)) {\n lastUserMessage = message;\n }\n }\n\n if (!lastUserMessage) {\n throw new Error(\"No user message found in request messages\");\n }\n\n const modelInstance = !model\n ? (request.model as BaseLanguageModel)\n : typeof model === \"string\"\n ? await initChatModel(model)\n : model;\n\n const validToolNames = availableTools.map((tool) => tool.name);\n\n return {\n availableTools,\n systemMessage,\n lastUserMessage,\n model: modelInstance,\n validToolNames,\n };\n}\n\n/**\n * Process the selection response and return filtered ModelRequest.\n *\n * @param response - The structured output response from the model.\n * @param availableTools - Tools available for selection.\n * @param validToolNames - Valid tool names that can be selected.\n * @param request - Original model request.\n * @param options - Configuration options.\n * @returns Modified ModelRequest with filtered tools.\n */\nfunction processSelectionResponse<\n TState extends Record<string, unknown> = Record<string, unknown>,\n TContext = unknown,\n>(\n response: { tools: string[] },\n availableTools: StructuredToolInterface[],\n validToolNames: string[],\n request: ModelRequest<TState, TContext>,\n options: LLMToolSelectorConfig\n): ModelRequest<TState, TContext> {\n const maxTools = options.maxTools;\n const alwaysInclude = options.alwaysInclude ?? [];\n\n const selectedToolNames: string[] = [];\n const invalidToolSelections: string[] = [];\n\n for (const toolName of response.tools) {\n if (!validToolNames.includes(toolName)) {\n invalidToolSelections.push(toolName);\n continue;\n }\n\n /**\n * Only add if not already selected and within maxTools limit\n */\n if (\n !selectedToolNames.includes(toolName) &&\n (maxTools === undefined || selectedToolNames.length < maxTools)\n ) {\n selectedToolNames.push(toolName);\n }\n }\n\n if (invalidToolSelections.length > 0) {\n throw new Error(\n `Model selected invalid tools: ${invalidToolSelections.join(\", \")}`\n );\n }\n\n /**\n * Filter tools based on selection\n */\n const selectedTools = availableTools.filter((tool) =>\n selectedToolNames.includes(tool.name)\n );\n\n /**\n * Append always-included tools\n */\n const alwaysIncludedTools = (request.tools ?? []).filter(\n (tool): tool is StructuredToolInterface =>\n typeof tool === \"object\" &&\n \"name\" in tool &&\n typeof tool.name === \"string\" &&\n alwaysInclude.includes(tool.name)\n );\n selectedTools.push(...alwaysIncludedTools);\n\n /**\n * Also preserve any provider-specific tool dicts from the original request\n */\n const providerTools = (request.tools ?? []).filter(\n (tool) =>\n !(\n typeof tool === \"object\" &&\n \"name\" in tool &&\n \"description\" in tool &&\n typeof tool.name === \"string\"\n )\n );\n\n return {\n ...request,\n tools: [...selectedTools, ...providerTools],\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llmToolSelector.d.cts","names":[],"sources":["../../../src/agents/middleware/llmToolSelector.ts"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"llmToolSelector.d.cts","names":[],"sources":["../../../src/agents/middleware/llmToolSelector.ts"],"mappings":";;;;;;;;cA8Da,8BAA4B,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAoB7B,wBAAwB,4BAC3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyCO,0BAA0B,SAAS,mDAAqB,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6CAyDvE,6CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llmToolSelector.d.ts","names":[],"sources":["../../../src/agents/middleware/llmToolSelector.ts"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"llmToolSelector.d.ts","names":[],"sources":["../../../src/agents/middleware/llmToolSelector.ts"],"mappings":";;;;;;;;cA8Da,8BAA4B,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAoB7B,wBAAwB,4BAC3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyCO,0BAA0B,SAAS,mDAAqB,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6CAyDvE,6CAAA"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { initChatModel } from "../../chat_models/universal.js";
|
|
2
2
|
import { createMiddleware } from "../middleware.js";
|
|
3
|
+
import { INTERNAL_CALL_TAG } from "./constants.js";
|
|
3
4
|
import { HumanMessage } from "@langchain/core/messages";
|
|
4
5
|
import { mergeConfigs, pickRunnableConfigKeys } from "@langchain/core/runnables";
|
|
5
6
|
import { z } from "zod/v3";
|
|
@@ -90,6 +91,7 @@ function llmToolSelectorMiddleware(options) {
|
|
|
90
91
|
const structuredModel = await selectionRequest.model.withStructuredOutput?.(toolSelectionSchema);
|
|
91
92
|
const config = mergeConfigs(pickRunnableConfigKeys(request.runtime) ?? {}, {
|
|
92
93
|
metadata: { lc_source: "llmToolSelector" },
|
|
94
|
+
tags: [INTERNAL_CALL_TAG],
|
|
93
95
|
callbacks: []
|
|
94
96
|
});
|
|
95
97
|
const response = await structuredModel?.invoke([{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llmToolSelector.js","names":[],"sources":["../../../src/agents/middleware/llmToolSelector.ts"],"sourcesContent":["import { z } from \"zod/v3\";\nimport { BaseLanguageModel } from \"@langchain/core/language_models/base\";\nimport type { InferInteropZodInput } from \"@langchain/core/utils/types\";\nimport { HumanMessage } from \"@langchain/core/messages\";\nimport type { StructuredToolInterface } from \"@langchain/core/tools\";\n\nimport { createMiddleware } from \"../middleware.js\";\nimport { initChatModel } from \"../../chat_models/universal.js\";\nimport type { Runtime } from \"../runtime.js\";\nimport type { ModelRequest } from \"../nodes/types.js\";\nimport {\n mergeConfigs,\n pickRunnableConfigKeys,\n type RunnableConfig,\n} from \"@langchain/core/runnables\";\n\nconst DEFAULT_SYSTEM_PROMPT =\n \"Your goal is to select the most relevant tools for answering the user's query.\";\n\n/**\n * Prepared inputs for tool selection.\n */\ninterface SelectionRequest {\n availableTools: StructuredToolInterface[];\n systemMessage: string;\n lastUserMessage: HumanMessage;\n model: BaseLanguageModel;\n validToolNames: string[];\n}\n\n/**\n * Create a structured output schema for tool selection.\n *\n * @param tools - Available tools to include in the schema.\n * @returns Zod schema where each tool name is a literal with its description.\n */\nfunction createToolSelectionResponse(tools: StructuredToolInterface[]) {\n if (!tools || tools.length === 0) {\n throw new Error(\"Invalid usage: tools must be non-empty\");\n }\n\n // Create a union of literals for each tool name\n const toolLiterals = tools.map((tool) => z.literal(tool.name));\n const toolEnum = z.union(\n toolLiterals as [\n z.ZodLiteral<string>,\n z.ZodLiteral<string>,\n ...z.ZodLiteral<string>[],\n ]\n );\n\n return z.object({\n tools: z\n .array(toolEnum)\n .describe(\"Tools to use. Place the most relevant tools first.\"),\n });\n}\n\n/**\n * Options for configuring the LLM Tool Selector middleware.\n */\nexport const LLMToolSelectorOptionsSchema = z.object({\n /**\n * The language model to use for tool selection (default: the provided model from the agent options).\n */\n model: z.string().or(z.instanceof(BaseLanguageModel)).optional(),\n /**\n * System prompt for the tool selection model.\n */\n systemPrompt: z.string().optional(),\n /**\n * Maximum number of tools to select. If the model selects more,\n * only the first maxTools will be used. No limit if not specified.\n */\n maxTools: z.number().optional(),\n /**\n * Tool names to always include regardless of selection.\n * These do not count against the maxTools limit.\n */\n alwaysInclude: z.array(z.string()).optional(),\n});\nexport type LLMToolSelectorConfig = InferInteropZodInput<\n typeof LLMToolSelectorOptionsSchema\n>;\n\n/**\n * Middleware for selecting tools using an LLM-based strategy.\n *\n * When an agent has many tools available, this middleware filters them down\n * to only the most relevant ones for the user's query. This reduces token usage\n * and helps the main model focus on the right tools.\n *\n * @param options - Configuration options for the middleware\n * @param options.model - The language model to use for tool selection (default: the provided model from the agent options).\n * @param options.systemPrompt - Instructions for the selection model.\n * @param options.maxTools - Maximum number of tools to select. If the model selects more,\n * only the first maxTools will be used. No limit if not specified.\n * @param options.alwaysInclude - Tool names to always include regardless of selection.\n * These do not count against the maxTools limit.\n *\n * @example\n * Limit to 3 tools:\n * ```ts\n * import { llmToolSelectorMiddleware } from \"langchain/agents/middleware\";\n *\n * const middleware = llmToolSelectorMiddleware({ maxTools: 3 });\n *\n * const agent = createAgent({\n * model: \"openai:gpt-4o\",\n * tools: [tool1, tool2, tool3, tool4, tool5],\n * middleware: [middleware],\n * });\n * ```\n *\n * @example\n * Use a smaller model for selection:\n * ```ts\n * const middleware = llmToolSelectorMiddleware({\n * model: \"openai:gpt-4o-mini\",\n * maxTools: 2\n * });\n * ```\n */\nexport function llmToolSelectorMiddleware(options: LLMToolSelectorConfig) {\n return createMiddleware({\n name: \"LLMToolSelector\",\n contextSchema: LLMToolSelectorOptionsSchema,\n async wrapModelCall(request, handler) {\n const selectionRequest = await prepareSelectionRequest(\n request,\n options,\n request.runtime\n );\n if (!selectionRequest) {\n return handler(request);\n }\n\n // Create dynamic response model with union of literal tool names\n const toolSelectionSchema = createToolSelectionResponse(\n selectionRequest.availableTools\n );\n const structuredModel =\n await selectionRequest.model.withStructuredOutput?.(\n toolSelectionSchema\n );\n\n const baseConfig: RunnableConfig =\n pickRunnableConfigKeys(request.runtime) ?? {};\n const config = mergeConfigs(baseConfig, {\n metadata: { lc_source: \"llmToolSelector\" },\n callbacks: [],\n });\n\n const response = await structuredModel?.invoke(\n [\n { role: \"system\", content: selectionRequest.systemMessage },\n selectionRequest.lastUserMessage,\n ],\n config\n );\n\n // Response should be an object with a tools array\n if (!response || typeof response !== \"object\" || !(\"tools\" in response)) {\n throw new Error(\n `Expected object response with tools array, got ${typeof response}`\n );\n }\n\n return handler(\n processSelectionResponse(\n response as { tools: string[] },\n selectionRequest.availableTools,\n selectionRequest.validToolNames,\n request,\n options\n )\n );\n },\n });\n}\n\n/**\n * Prepare inputs for tool selection.\n *\n * @param request - The model request to process.\n * @param options - Configuration options.\n * @param runtime - Runtime context.\n * @returns SelectionRequest with prepared inputs, or null if no selection is needed.\n */\nasync function prepareSelectionRequest<\n TState extends Record<string, unknown> = Record<string, unknown>,\n TContext = unknown,\n>(\n request: ModelRequest<TState, TContext>,\n options: LLMToolSelectorConfig,\n runtime: Runtime<LLMToolSelectorConfig>\n): Promise<SelectionRequest | undefined> {\n const model = runtime.context.model ?? options.model;\n const maxTools = runtime.context.maxTools ?? options.maxTools;\n const alwaysInclude =\n runtime.context.alwaysInclude ?? options.alwaysInclude ?? [];\n const systemPrompt =\n runtime.context.systemPrompt ??\n options.systemPrompt ??\n DEFAULT_SYSTEM_PROMPT;\n\n /**\n * If no tools available, return null\n */\n if (!request.tools || request.tools.length === 0) {\n return undefined;\n }\n\n /**\n * Filter to only StructuredToolInterface instances (exclude provider-specific tool dicts)\n */\n const baseTools = request.tools.filter(\n (tool): tool is StructuredToolInterface =>\n typeof tool === \"object\" &&\n \"name\" in tool &&\n \"description\" in tool &&\n typeof tool.name === \"string\"\n );\n\n /**\n * Validate that alwaysInclude tools exist\n */\n if (alwaysInclude.length > 0) {\n const availableToolNames = new Set(baseTools.map((tool) => tool.name));\n const missingTools = alwaysInclude.filter(\n (name) => !availableToolNames.has(name)\n );\n if (missingTools.length > 0) {\n throw new Error(\n `Tools in alwaysInclude not found in request: ${missingTools.join(\n \", \"\n )}. ` +\n `Available tools: ${Array.from(availableToolNames).sort().join(\", \")}`\n );\n }\n }\n\n /**\n * Separate tools that are always included from those available for selection\n */\n const availableTools = baseTools.filter(\n (tool) => !alwaysInclude.includes(tool.name)\n );\n\n /**\n * If no tools available for selection, return null\n */\n if (availableTools.length === 0) {\n return undefined;\n }\n\n let systemMessage = systemPrompt;\n /**\n * If there's a maxTools limit, append instructions to the system prompt\n */\n if (maxTools !== undefined) {\n systemMessage +=\n `\\nIMPORTANT: List the tool names in order of relevance, ` +\n `with the most relevant first. ` +\n `If you exceed the maximum number of tools, ` +\n `only the first ${maxTools} will be used.`;\n }\n\n /**\n * Get the last user message from the conversation history\n */\n let lastUserMessage: HumanMessage | undefined;\n for (const message of request.messages) {\n if (HumanMessage.isInstance(message)) {\n lastUserMessage = message;\n }\n }\n\n if (!lastUserMessage) {\n throw new Error(\"No user message found in request messages\");\n }\n\n const modelInstance = !model\n ? (request.model as BaseLanguageModel)\n : typeof model === \"string\"\n ? await initChatModel(model)\n : model;\n\n const validToolNames = availableTools.map((tool) => tool.name);\n\n return {\n availableTools,\n systemMessage,\n lastUserMessage,\n model: modelInstance,\n validToolNames,\n };\n}\n\n/**\n * Process the selection response and return filtered ModelRequest.\n *\n * @param response - The structured output response from the model.\n * @param availableTools - Tools available for selection.\n * @param validToolNames - Valid tool names that can be selected.\n * @param request - Original model request.\n * @param options - Configuration options.\n * @returns Modified ModelRequest with filtered tools.\n */\nfunction processSelectionResponse<\n TState extends Record<string, unknown> = Record<string, unknown>,\n TContext = unknown,\n>(\n response: { tools: string[] },\n availableTools: StructuredToolInterface[],\n validToolNames: string[],\n request: ModelRequest<TState, TContext>,\n options: LLMToolSelectorConfig\n): ModelRequest<TState, TContext> {\n const maxTools = options.maxTools;\n const alwaysInclude = options.alwaysInclude ?? [];\n\n const selectedToolNames: string[] = [];\n const invalidToolSelections: string[] = [];\n\n for (const toolName of response.tools) {\n if (!validToolNames.includes(toolName)) {\n invalidToolSelections.push(toolName);\n continue;\n }\n\n /**\n * Only add if not already selected and within maxTools limit\n */\n if (\n !selectedToolNames.includes(toolName) &&\n (maxTools === undefined || selectedToolNames.length < maxTools)\n ) {\n selectedToolNames.push(toolName);\n }\n }\n\n if (invalidToolSelections.length > 0) {\n throw new Error(\n `Model selected invalid tools: ${invalidToolSelections.join(\", \")}`\n );\n }\n\n /**\n * Filter tools based on selection\n */\n const selectedTools = availableTools.filter((tool) =>\n selectedToolNames.includes(tool.name)\n );\n\n /**\n * Append always-included tools\n */\n const alwaysIncludedTools = (request.tools ?? []).filter(\n (tool): tool is StructuredToolInterface =>\n typeof tool === \"object\" &&\n \"name\" in tool &&\n typeof tool.name === \"string\" &&\n alwaysInclude.includes(tool.name)\n );\n selectedTools.push(...alwaysIncludedTools);\n\n /**\n * Also preserve any provider-specific tool dicts from the original request\n */\n const providerTools = (request.tools ?? []).filter(\n (tool) =>\n !(\n typeof tool === \"object\" &&\n \"name\" in tool &&\n \"description\" in tool &&\n typeof tool.name === \"string\"\n )\n );\n\n return {\n ...request,\n tools: [...selectedTools, ...providerTools],\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"llmToolSelector.js","names":[],"sources":["../../../src/agents/middleware/llmToolSelector.ts"],"sourcesContent":["import { z } from \"zod/v3\";\nimport { BaseLanguageModel } from \"@langchain/core/language_models/base\";\nimport type { InferInteropZodInput } from \"@langchain/core/utils/types\";\nimport { HumanMessage } from \"@langchain/core/messages\";\nimport type { StructuredToolInterface } from \"@langchain/core/tools\";\n\nimport { createMiddleware } from \"../middleware.js\";\nimport { INTERNAL_CALL_TAG } from \"./constants.js\";\nimport { initChatModel } from \"../../chat_models/universal.js\";\nimport type { Runtime } from \"../runtime.js\";\nimport type { ModelRequest } from \"../nodes/types.js\";\nimport {\n mergeConfigs,\n pickRunnableConfigKeys,\n type RunnableConfig,\n} from \"@langchain/core/runnables\";\n\nconst DEFAULT_SYSTEM_PROMPT =\n \"Your goal is to select the most relevant tools for answering the user's query.\";\n\n/**\n * Prepared inputs for tool selection.\n */\ninterface SelectionRequest {\n availableTools: StructuredToolInterface[];\n systemMessage: string;\n lastUserMessage: HumanMessage;\n model: BaseLanguageModel;\n validToolNames: string[];\n}\n\n/**\n * Create a structured output schema for tool selection.\n *\n * @param tools - Available tools to include in the schema.\n * @returns Zod schema where each tool name is a literal with its description.\n */\nfunction createToolSelectionResponse(tools: StructuredToolInterface[]) {\n if (!tools || tools.length === 0) {\n throw new Error(\"Invalid usage: tools must be non-empty\");\n }\n\n // Create a union of literals for each tool name\n const toolLiterals = tools.map((tool) => z.literal(tool.name));\n const toolEnum = z.union(\n toolLiterals as [\n z.ZodLiteral<string>,\n z.ZodLiteral<string>,\n ...z.ZodLiteral<string>[],\n ]\n );\n\n return z.object({\n tools: z\n .array(toolEnum)\n .describe(\"Tools to use. Place the most relevant tools first.\"),\n });\n}\n\n/**\n * Options for configuring the LLM Tool Selector middleware.\n */\nexport const LLMToolSelectorOptionsSchema = z.object({\n /**\n * The language model to use for tool selection (default: the provided model from the agent options).\n */\n model: z.string().or(z.instanceof(BaseLanguageModel)).optional(),\n /**\n * System prompt for the tool selection model.\n */\n systemPrompt: z.string().optional(),\n /**\n * Maximum number of tools to select. If the model selects more,\n * only the first maxTools will be used. No limit if not specified.\n */\n maxTools: z.number().optional(),\n /**\n * Tool names to always include regardless of selection.\n * These do not count against the maxTools limit.\n */\n alwaysInclude: z.array(z.string()).optional(),\n});\nexport type LLMToolSelectorConfig = InferInteropZodInput<\n typeof LLMToolSelectorOptionsSchema\n>;\n\n/**\n * Middleware for selecting tools using an LLM-based strategy.\n *\n * When an agent has many tools available, this middleware filters them down\n * to only the most relevant ones for the user's query. This reduces token usage\n * and helps the main model focus on the right tools.\n *\n * @param options - Configuration options for the middleware\n * @param options.model - The language model to use for tool selection (default: the provided model from the agent options).\n * @param options.systemPrompt - Instructions for the selection model.\n * @param options.maxTools - Maximum number of tools to select. If the model selects more,\n * only the first maxTools will be used. No limit if not specified.\n * @param options.alwaysInclude - Tool names to always include regardless of selection.\n * These do not count against the maxTools limit.\n *\n * @example\n * Limit to 3 tools:\n * ```ts\n * import { llmToolSelectorMiddleware } from \"langchain/agents/middleware\";\n *\n * const middleware = llmToolSelectorMiddleware({ maxTools: 3 });\n *\n * const agent = createAgent({\n * model: \"openai:gpt-4o\",\n * tools: [tool1, tool2, tool3, tool4, tool5],\n * middleware: [middleware],\n * });\n * ```\n *\n * @example\n * Use a smaller model for selection:\n * ```ts\n * const middleware = llmToolSelectorMiddleware({\n * model: \"openai:gpt-4o-mini\",\n * maxTools: 2\n * });\n * ```\n */\nexport function llmToolSelectorMiddleware(options: LLMToolSelectorConfig) {\n return createMiddleware({\n name: \"LLMToolSelector\",\n contextSchema: LLMToolSelectorOptionsSchema,\n async wrapModelCall(request, handler) {\n const selectionRequest = await prepareSelectionRequest(\n request,\n options,\n request.runtime\n );\n if (!selectionRequest) {\n return handler(request);\n }\n\n // Create dynamic response model with union of literal tool names\n const toolSelectionSchema = createToolSelectionResponse(\n selectionRequest.availableTools\n );\n const structuredModel =\n await selectionRequest.model.withStructuredOutput?.(\n toolSelectionSchema\n );\n\n const baseConfig: RunnableConfig =\n pickRunnableConfigKeys(request.runtime) ?? {};\n const config = mergeConfigs(baseConfig, {\n metadata: { lc_source: \"llmToolSelector\" },\n tags: [INTERNAL_CALL_TAG],\n callbacks: [],\n });\n\n const response = await structuredModel?.invoke(\n [\n { role: \"system\", content: selectionRequest.systemMessage },\n selectionRequest.lastUserMessage,\n ],\n config\n );\n\n // Response should be an object with a tools array\n if (!response || typeof response !== \"object\" || !(\"tools\" in response)) {\n throw new Error(\n `Expected object response with tools array, got ${typeof response}`\n );\n }\n\n return handler(\n processSelectionResponse(\n response as { tools: string[] },\n selectionRequest.availableTools,\n selectionRequest.validToolNames,\n request,\n options\n )\n );\n },\n });\n}\n\n/**\n * Prepare inputs for tool selection.\n *\n * @param request - The model request to process.\n * @param options - Configuration options.\n * @param runtime - Runtime context.\n * @returns SelectionRequest with prepared inputs, or null if no selection is needed.\n */\nasync function prepareSelectionRequest<\n TState extends Record<string, unknown> = Record<string, unknown>,\n TContext = unknown,\n>(\n request: ModelRequest<TState, TContext>,\n options: LLMToolSelectorConfig,\n runtime: Runtime<LLMToolSelectorConfig>\n): Promise<SelectionRequest | undefined> {\n const model = runtime.context.model ?? options.model;\n const maxTools = runtime.context.maxTools ?? options.maxTools;\n const alwaysInclude =\n runtime.context.alwaysInclude ?? options.alwaysInclude ?? [];\n const systemPrompt =\n runtime.context.systemPrompt ??\n options.systemPrompt ??\n DEFAULT_SYSTEM_PROMPT;\n\n /**\n * If no tools available, return null\n */\n if (!request.tools || request.tools.length === 0) {\n return undefined;\n }\n\n /**\n * Filter to only StructuredToolInterface instances (exclude provider-specific tool dicts)\n */\n const baseTools = request.tools.filter(\n (tool): tool is StructuredToolInterface =>\n typeof tool === \"object\" &&\n \"name\" in tool &&\n \"description\" in tool &&\n typeof tool.name === \"string\"\n );\n\n /**\n * Validate that alwaysInclude tools exist\n */\n if (alwaysInclude.length > 0) {\n const availableToolNames = new Set(baseTools.map((tool) => tool.name));\n const missingTools = alwaysInclude.filter(\n (name) => !availableToolNames.has(name)\n );\n if (missingTools.length > 0) {\n throw new Error(\n `Tools in alwaysInclude not found in request: ${missingTools.join(\n \", \"\n )}. ` +\n `Available tools: ${Array.from(availableToolNames).sort().join(\", \")}`\n );\n }\n }\n\n /**\n * Separate tools that are always included from those available for selection\n */\n const availableTools = baseTools.filter(\n (tool) => !alwaysInclude.includes(tool.name)\n );\n\n /**\n * If no tools available for selection, return null\n */\n if (availableTools.length === 0) {\n return undefined;\n }\n\n let systemMessage = systemPrompt;\n /**\n * If there's a maxTools limit, append instructions to the system prompt\n */\n if (maxTools !== undefined) {\n systemMessage +=\n `\\nIMPORTANT: List the tool names in order of relevance, ` +\n `with the most relevant first. ` +\n `If you exceed the maximum number of tools, ` +\n `only the first ${maxTools} will be used.`;\n }\n\n /**\n * Get the last user message from the conversation history\n */\n let lastUserMessage: HumanMessage | undefined;\n for (const message of request.messages) {\n if (HumanMessage.isInstance(message)) {\n lastUserMessage = message;\n }\n }\n\n if (!lastUserMessage) {\n throw new Error(\"No user message found in request messages\");\n }\n\n const modelInstance = !model\n ? (request.model as BaseLanguageModel)\n : typeof model === \"string\"\n ? await initChatModel(model)\n : model;\n\n const validToolNames = availableTools.map((tool) => tool.name);\n\n return {\n availableTools,\n systemMessage,\n lastUserMessage,\n model: modelInstance,\n validToolNames,\n };\n}\n\n/**\n * Process the selection response and return filtered ModelRequest.\n *\n * @param response - The structured output response from the model.\n * @param availableTools - Tools available for selection.\n * @param validToolNames - Valid tool names that can be selected.\n * @param request - Original model request.\n * @param options - Configuration options.\n * @returns Modified ModelRequest with filtered tools.\n */\nfunction processSelectionResponse<\n TState extends Record<string, unknown> = Record<string, unknown>,\n TContext = unknown,\n>(\n response: { tools: string[] },\n availableTools: StructuredToolInterface[],\n validToolNames: string[],\n request: ModelRequest<TState, TContext>,\n options: LLMToolSelectorConfig\n): ModelRequest<TState, TContext> {\n const maxTools = options.maxTools;\n const alwaysInclude = options.alwaysInclude ?? [];\n\n const selectedToolNames: string[] = [];\n const invalidToolSelections: string[] = [];\n\n for (const toolName of response.tools) {\n if (!validToolNames.includes(toolName)) {\n invalidToolSelections.push(toolName);\n continue;\n }\n\n /**\n * Only add if not already selected and within maxTools limit\n */\n if (\n !selectedToolNames.includes(toolName) &&\n (maxTools === undefined || selectedToolNames.length < maxTools)\n ) {\n selectedToolNames.push(toolName);\n }\n }\n\n if (invalidToolSelections.length > 0) {\n throw new Error(\n `Model selected invalid tools: ${invalidToolSelections.join(\", \")}`\n );\n }\n\n /**\n * Filter tools based on selection\n */\n const selectedTools = availableTools.filter((tool) =>\n selectedToolNames.includes(tool.name)\n );\n\n /**\n * Append always-included tools\n */\n const alwaysIncludedTools = (request.tools ?? []).filter(\n (tool): tool is StructuredToolInterface =>\n typeof tool === \"object\" &&\n \"name\" in tool &&\n typeof tool.name === \"string\" &&\n alwaysInclude.includes(tool.name)\n );\n selectedTools.push(...alwaysIncludedTools);\n\n /**\n * Also preserve any provider-specific tool dicts from the original request\n */\n const providerTools = (request.tools ?? []).filter(\n (tool) =>\n !(\n typeof tool === \"object\" &&\n \"name\" in tool &&\n \"description\" in tool &&\n typeof tool.name === \"string\"\n )\n );\n\n return {\n ...request,\n tools: [...selectedTools, ...providerTools],\n };\n}\n"],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ const require_chat_models_universal = require("../../chat_models/universal.cjs")
|
|
|
2
2
|
const require_utils = require("../utils.cjs");
|
|
3
3
|
const require_utils$1 = require("./utils.cjs");
|
|
4
4
|
const require_middleware = require("../middleware.cjs");
|
|
5
|
+
const require_constants = require("./constants.cjs");
|
|
5
6
|
let _langchain_core_messages = require("@langchain/core/messages");
|
|
6
7
|
let _langchain_core_runnables = require("@langchain/core/runnables");
|
|
7
8
|
let _langchain_langgraph = require("@langchain/langgraph");
|
|
@@ -260,7 +261,7 @@ model: zod_v3.z.custom().optional() }),
|
|
|
260
261
|
const { messagesToSummarize, preservedMessages } = partitionMessages(systemPrompt, conversationMessages, cutoffIndex);
|
|
261
262
|
const summaryMessage = new _langchain_core_messages.HumanMessage({
|
|
262
263
|
content: `${summaryPrefix}\n\n${await createSummary(messagesToSummarize, model, summaryPrompt, tokenCounter, trimTokensToSummarize, runtime)}`,
|
|
263
|
-
id:
|
|
264
|
+
id: conversationMessages[0].id,
|
|
264
265
|
additional_kwargs: { lc_source: "summarization" }
|
|
265
266
|
});
|
|
266
267
|
return { messages: [
|
|
@@ -533,7 +534,10 @@ async function createSummary(messagesToSummarize, model, summaryPrompt, tokenCou
|
|
|
533
534
|
const formattedMessages = (0, _langchain_core_messages.getBufferString)(trimmedMessages);
|
|
534
535
|
try {
|
|
535
536
|
const formattedPrompt = summaryPrompt.replace("{messages}", formattedMessages);
|
|
536
|
-
const config = (0, _langchain_core_runnables.mergeConfigs)((0, _langchain_core_runnables.pickRunnableConfigKeys)(runtime) ?? {}, {
|
|
537
|
+
const config = (0, _langchain_core_runnables.mergeConfigs)((0, _langchain_core_runnables.pickRunnableConfigKeys)(runtime) ?? {}, {
|
|
538
|
+
metadata: { lc_source: "summarization" },
|
|
539
|
+
tags: [require_constants.INTERNAL_CALL_TAG]
|
|
540
|
+
});
|
|
537
541
|
const content = (await model.invoke(formattedPrompt, config)).content;
|
|
538
542
|
/**
|
|
539
543
|
* Handle both string content and MessageContent array
|