langchain 1.5.4 → 1.5.6
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/ReactAgent.cjs +6 -6
- package/dist/agents/ReactAgent.cjs.map +1 -1
- package/dist/agents/ReactAgent.d.cts +1 -1
- package/dist/agents/ReactAgent.d.cts.map +1 -1
- package/dist/agents/ReactAgent.d.ts +1 -1
- package/dist/agents/ReactAgent.d.ts.map +1 -1
- package/dist/agents/ReactAgent.js +6 -5
- package/dist/agents/ReactAgent.js.map +1 -1
- package/dist/agents/RunnableCallable.cjs +0 -1
- package/dist/agents/RunnableCallable.cjs.map +1 -1
- package/dist/agents/RunnableCallable.js.map +1 -1
- package/dist/agents/annotation.cjs +0 -1
- package/dist/agents/annotation.cjs.map +1 -1
- package/dist/agents/annotation.js.map +1 -1
- package/dist/agents/constants.d.cts +1 -1
- package/dist/agents/constants.d.cts.map +1 -1
- package/dist/agents/constants.d.ts +1 -1
- package/dist/agents/constants.d.ts.map +1 -1
- package/dist/agents/errors.cjs +6 -1
- package/dist/agents/errors.cjs.map +1 -1
- package/dist/agents/errors.d.cts +2 -1
- package/dist/agents/errors.d.cts.map +1 -1
- package/dist/agents/errors.d.ts +2 -1
- 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/index.cjs +21 -7
- package/dist/agents/index.cjs.map +1 -1
- package/dist/agents/index.d.cts +2 -2
- package/dist/agents/index.d.cts.map +1 -1
- package/dist/agents/index.d.ts +2 -2
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +8 -8
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/middleware/constants.cjs +3 -1
- 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/contextEditing.cjs +0 -1
- package/dist/agents/middleware/contextEditing.cjs.map +1 -1
- package/dist/agents/middleware/contextEditing.d.cts +1 -3
- package/dist/agents/middleware/contextEditing.d.cts.map +1 -1
- package/dist/agents/middleware/contextEditing.d.ts +1 -3
- package/dist/agents/middleware/contextEditing.d.ts.map +1 -1
- package/dist/agents/middleware/contextEditing.js.map +1 -1
- package/dist/agents/middleware/dynamicSystemPrompt.cjs +0 -1
- package/dist/agents/middleware/dynamicSystemPrompt.cjs.map +1 -1
- package/dist/agents/middleware/dynamicSystemPrompt.d.cts +1 -3
- package/dist/agents/middleware/dynamicSystemPrompt.d.cts.map +1 -1
- package/dist/agents/middleware/dynamicSystemPrompt.d.ts +1 -3
- package/dist/agents/middleware/dynamicSystemPrompt.d.ts.map +1 -1
- package/dist/agents/middleware/dynamicSystemPrompt.js.map +1 -1
- package/dist/agents/middleware/error.cjs +0 -1
- package/dist/agents/middleware/error.cjs.map +1 -1
- package/dist/agents/middleware/error.js.map +1 -1
- package/dist/agents/middleware/hitl.cjs +0 -1
- package/dist/agents/middleware/hitl.cjs.map +1 -1
- package/dist/agents/middleware/hitl.d.cts +1 -3
- package/dist/agents/middleware/hitl.d.cts.map +1 -1
- package/dist/agents/middleware/hitl.d.ts +1 -3
- package/dist/agents/middleware/hitl.d.ts.map +1 -1
- package/dist/agents/middleware/hitl.js.map +1 -1
- package/dist/agents/middleware/index.cjs +51 -20
- package/dist/agents/middleware/index.d.cts +2 -1
- package/dist/agents/middleware/index.d.ts +2 -1
- package/dist/agents/middleware/index.js +21 -21
- package/dist/agents/middleware/llmToolSelector.cjs +3 -1
- package/dist/agents/middleware/llmToolSelector.cjs.map +1 -1
- package/dist/agents/middleware/llmToolSelector.d.cts +9 -12
- package/dist/agents/middleware/llmToolSelector.d.cts.map +1 -1
- package/dist/agents/middleware/llmToolSelector.d.ts +9 -12
- package/dist/agents/middleware/llmToolSelector.d.ts.map +1 -1
- package/dist/agents/middleware/llmToolSelector.js +3 -1
- package/dist/agents/middleware/llmToolSelector.js.map +1 -1
- package/dist/agents/middleware/modelCallLimit.cjs +0 -1
- package/dist/agents/middleware/modelCallLimit.cjs.map +1 -1
- package/dist/agents/middleware/modelCallLimit.d.cts +1 -3
- package/dist/agents/middleware/modelCallLimit.d.cts.map +1 -1
- package/dist/agents/middleware/modelCallLimit.d.ts +1 -3
- package/dist/agents/middleware/modelCallLimit.d.ts.map +1 -1
- package/dist/agents/middleware/modelCallLimit.js.map +1 -1
- package/dist/agents/middleware/modelFallback.cjs.map +1 -1
- package/dist/agents/middleware/modelFallback.d.cts +1 -3
- package/dist/agents/middleware/modelFallback.d.cts.map +1 -1
- package/dist/agents/middleware/modelFallback.d.ts +1 -3
- package/dist/agents/middleware/modelFallback.d.ts.map +1 -1
- package/dist/agents/middleware/modelFallback.js.map +1 -1
- package/dist/agents/middleware/modelRetry.cjs +1 -1
- package/dist/agents/middleware/modelRetry.cjs.map +1 -1
- package/dist/agents/middleware/modelRetry.d.cts +2 -4
- package/dist/agents/middleware/modelRetry.d.cts.map +1 -1
- package/dist/agents/middleware/modelRetry.d.ts +2 -4
- package/dist/agents/middleware/modelRetry.d.ts.map +1 -1
- package/dist/agents/middleware/modelRetry.js +1 -1
- package/dist/agents/middleware/modelRetry.js.map +1 -1
- package/dist/agents/middleware/pii.cjs +2 -1
- package/dist/agents/middleware/pii.cjs.map +1 -1
- package/dist/agents/middleware/pii.d.cts +2 -4
- package/dist/agents/middleware/pii.d.cts.map +1 -1
- package/dist/agents/middleware/pii.d.ts +2 -4
- package/dist/agents/middleware/pii.d.ts.map +1 -1
- package/dist/agents/middleware/pii.js +2 -0
- package/dist/agents/middleware/pii.js.map +1 -1
- package/dist/agents/middleware/piiRedaction.cjs +0 -1
- package/dist/agents/middleware/piiRedaction.cjs.map +1 -1
- package/dist/agents/middleware/piiRedaction.d.cts +1 -3
- package/dist/agents/middleware/piiRedaction.d.cts.map +1 -1
- package/dist/agents/middleware/piiRedaction.d.ts +1 -3
- package/dist/agents/middleware/piiRedaction.d.ts.map +1 -1
- package/dist/agents/middleware/piiRedaction.js.map +1 -1
- package/dist/agents/middleware/provider/anthropic/promptCaching.cjs +0 -1
- package/dist/agents/middleware/provider/anthropic/promptCaching.cjs.map +1 -1
- package/dist/agents/middleware/provider/anthropic/promptCaching.d.cts +1 -3
- package/dist/agents/middleware/provider/anthropic/promptCaching.d.cts.map +1 -1
- package/dist/agents/middleware/provider/anthropic/promptCaching.d.ts +1 -3
- package/dist/agents/middleware/provider/anthropic/promptCaching.d.ts.map +1 -1
- package/dist/agents/middleware/provider/anthropic/promptCaching.js.map +1 -1
- package/dist/agents/middleware/provider/aws/promptCaching.cjs +0 -1
- package/dist/agents/middleware/provider/aws/promptCaching.cjs.map +1 -1
- package/dist/agents/middleware/provider/aws/promptCaching.d.cts +1 -3
- package/dist/agents/middleware/provider/aws/promptCaching.d.cts.map +1 -1
- package/dist/agents/middleware/provider/aws/promptCaching.d.ts +1 -3
- package/dist/agents/middleware/provider/aws/promptCaching.d.ts.map +1 -1
- package/dist/agents/middleware/provider/aws/promptCaching.js.map +1 -1
- package/dist/agents/middleware/provider/openai/moderation.cjs +1 -1
- package/dist/agents/middleware/provider/openai/moderation.cjs.map +1 -1
- package/dist/agents/middleware/provider/openai/moderation.d.cts +0 -4
- package/dist/agents/middleware/provider/openai/moderation.d.cts.map +1 -1
- package/dist/agents/middleware/provider/openai/moderation.d.ts +0 -4
- package/dist/agents/middleware/provider/openai/moderation.d.ts.map +1 -1
- package/dist/agents/middleware/provider/openai/moderation.js +1 -1
- package/dist/agents/middleware/provider/openai/moderation.js.map +1 -1
- package/dist/agents/middleware/providerToolSearch.cjs +0 -1
- package/dist/agents/middleware/providerToolSearch.cjs.map +1 -1
- package/dist/agents/middleware/providerToolSearch.d.cts +1 -2
- package/dist/agents/middleware/providerToolSearch.d.cts.map +1 -1
- package/dist/agents/middleware/providerToolSearch.d.ts +1 -2
- package/dist/agents/middleware/providerToolSearch.d.ts.map +1 -1
- package/dist/agents/middleware/providerToolSearch.js.map +1 -1
- package/dist/agents/middleware/summarization.cjs +8 -5
- package/dist/agents/middleware/summarization.cjs.map +1 -1
- package/dist/agents/middleware/summarization.d.cts +16 -20
- package/dist/agents/middleware/summarization.d.cts.map +1 -1
- package/dist/agents/middleware/summarization.d.ts +16 -20
- package/dist/agents/middleware/summarization.d.ts.map +1 -1
- package/dist/agents/middleware/summarization.js +8 -5
- package/dist/agents/middleware/summarization.js.map +1 -1
- package/dist/agents/middleware/todoListMiddleware.cjs +0 -1
- package/dist/agents/middleware/todoListMiddleware.cjs.map +1 -1
- package/dist/agents/middleware/todoListMiddleware.d.cts +2 -5
- package/dist/agents/middleware/todoListMiddleware.d.cts.map +1 -1
- package/dist/agents/middleware/todoListMiddleware.d.ts +2 -5
- package/dist/agents/middleware/todoListMiddleware.d.ts.map +1 -1
- package/dist/agents/middleware/todoListMiddleware.js.map +1 -1
- package/dist/agents/middleware/toolCallLimit.cjs +0 -1
- package/dist/agents/middleware/toolCallLimit.cjs.map +1 -1
- package/dist/agents/middleware/toolCallLimit.d.cts +2 -4
- package/dist/agents/middleware/toolCallLimit.d.cts.map +1 -1
- package/dist/agents/middleware/toolCallLimit.d.ts +2 -4
- package/dist/agents/middleware/toolCallLimit.d.ts.map +1 -1
- package/dist/agents/middleware/toolCallLimit.js.map +1 -1
- package/dist/agents/middleware/toolEmulator.cjs +8 -2
- package/dist/agents/middleware/toolEmulator.cjs.map +1 -1
- package/dist/agents/middleware/toolEmulator.d.cts +0 -1
- package/dist/agents/middleware/toolEmulator.d.cts.map +1 -1
- package/dist/agents/middleware/toolEmulator.d.ts +0 -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/middleware/toolError.cjs +0 -1
- package/dist/agents/middleware/toolError.cjs.map +1 -1
- package/dist/agents/middleware/toolError.d.cts +0 -1
- package/dist/agents/middleware/toolError.d.cts.map +1 -1
- package/dist/agents/middleware/toolError.d.ts +0 -1
- package/dist/agents/middleware/toolError.d.ts.map +1 -1
- package/dist/agents/middleware/toolError.js.map +1 -1
- package/dist/agents/middleware/toolRetry.cjs +1 -1
- package/dist/agents/middleware/toolRetry.cjs.map +1 -1
- package/dist/agents/middleware/toolRetry.d.cts +1 -2
- package/dist/agents/middleware/toolRetry.d.cts.map +1 -1
- package/dist/agents/middleware/toolRetry.d.ts +1 -2
- package/dist/agents/middleware/toolRetry.d.ts.map +1 -1
- package/dist/agents/middleware/toolRetry.js +1 -1
- package/dist/agents/middleware/toolRetry.js.map +1 -1
- package/dist/agents/middleware/types.cjs.map +1 -1
- package/dist/agents/middleware/types.d.cts +1 -2
- package/dist/agents/middleware/types.d.cts.map +1 -1
- package/dist/agents/middleware/types.d.ts +1 -2
- package/dist/agents/middleware/types.d.ts.map +1 -1
- package/dist/agents/middleware/types.js.map +1 -1
- package/dist/agents/middleware/utils.cjs +0 -1
- package/dist/agents/middleware/utils.cjs.map +1 -1
- package/dist/agents/middleware/utils.d.cts +2 -1
- package/dist/agents/middleware/utils.d.cts.map +1 -1
- package/dist/agents/middleware/utils.d.ts +2 -1
- package/dist/agents/middleware/utils.d.ts.map +1 -1
- package/dist/agents/middleware/utils.js.map +1 -1
- package/dist/agents/middleware.cjs.map +1 -1
- package/dist/agents/middleware.d.cts +0 -1
- package/dist/agents/middleware.d.cts.map +1 -1
- package/dist/agents/middleware.d.ts +0 -1
- package/dist/agents/middleware.d.ts.map +1 -1
- package/dist/agents/middleware.js.map +1 -1
- package/dist/agents/model.cjs.map +1 -1
- package/dist/agents/model.d.cts +0 -1
- package/dist/agents/model.d.cts.map +1 -1
- package/dist/agents/model.d.ts +0 -1
- package/dist/agents/model.d.ts.map +1 -1
- package/dist/agents/model.js.map +1 -1
- package/dist/agents/nodes/AfterAgentNode.cjs +1 -0
- package/dist/agents/nodes/AfterAgentNode.cjs.map +1 -1
- package/dist/agents/nodes/AfterAgentNode.js +1 -0
- package/dist/agents/nodes/AfterAgentNode.js.map +1 -1
- package/dist/agents/nodes/AfterModelNode.cjs +1 -0
- package/dist/agents/nodes/AfterModelNode.cjs.map +1 -1
- package/dist/agents/nodes/AfterModelNode.js +1 -0
- package/dist/agents/nodes/AfterModelNode.js.map +1 -1
- package/dist/agents/nodes/AgentNode.cjs +0 -1
- package/dist/agents/nodes/AgentNode.cjs.map +1 -1
- package/dist/agents/nodes/AgentNode.js.map +1 -1
- package/dist/agents/nodes/BeforeAgentNode.cjs +1 -0
- package/dist/agents/nodes/BeforeAgentNode.cjs.map +1 -1
- package/dist/agents/nodes/BeforeAgentNode.js +1 -0
- package/dist/agents/nodes/BeforeAgentNode.js.map +1 -1
- package/dist/agents/nodes/BeforeModelNode.cjs +1 -0
- package/dist/agents/nodes/BeforeModelNode.cjs.map +1 -1
- package/dist/agents/nodes/BeforeModelNode.js +1 -0
- package/dist/agents/nodes/BeforeModelNode.js.map +1 -1
- package/dist/agents/nodes/ToolNode.cjs +2 -1
- package/dist/agents/nodes/ToolNode.cjs.map +1 -1
- package/dist/agents/nodes/ToolNode.js +2 -1
- package/dist/agents/nodes/ToolNode.js.map +1 -1
- package/dist/agents/nodes/middleware.cjs +0 -1
- package/dist/agents/nodes/middleware.cjs.map +1 -1
- package/dist/agents/nodes/middleware.js.map +1 -1
- package/dist/agents/nodes/types.d.cts +0 -1
- package/dist/agents/nodes/types.d.cts.map +1 -1
- package/dist/agents/nodes/types.d.ts +0 -1
- package/dist/agents/nodes/types.d.ts.map +1 -1
- package/dist/agents/nodes/utils.cjs +0 -1
- package/dist/agents/nodes/utils.cjs.map +1 -1
- package/dist/agents/nodes/utils.js.map +1 -1
- package/dist/agents/responses.cjs +11 -5
- package/dist/agents/responses.cjs.map +1 -1
- package/dist/agents/responses.d.cts +3 -3
- package/dist/agents/responses.d.cts.map +1 -1
- package/dist/agents/responses.d.ts +3 -3
- package/dist/agents/responses.d.ts.map +1 -1
- package/dist/agents/responses.js +11 -5
- package/dist/agents/responses.js.map +1 -1
- package/dist/agents/runtime.d.cts +2 -2
- package/dist/agents/runtime.d.cts.map +1 -1
- package/dist/agents/runtime.d.ts +2 -2
- package/dist/agents/runtime.d.ts.map +1 -1
- package/dist/agents/tests/utils.cjs +0 -1
- package/dist/agents/tests/utils.cjs.map +1 -1
- package/dist/agents/tests/utils.d.cts +2 -9
- package/dist/agents/tests/utils.d.cts.map +1 -1
- package/dist/agents/tests/utils.d.ts +2 -9
- package/dist/agents/tests/utils.d.ts.map +1 -1
- package/dist/agents/tests/utils.js.map +1 -1
- package/dist/agents/transformers/index.cjs +4 -2
- package/dist/agents/transformers/index.d.cts +2 -1
- package/dist/agents/transformers/index.d.ts +2 -1
- package/dist/agents/transformers/index.js +3 -3
- package/dist/agents/transformers/subagent.cjs +0 -1
- package/dist/agents/transformers/subagent.cjs.map +1 -1
- package/dist/agents/transformers/subagent.d.cts +0 -1
- package/dist/agents/transformers/subagent.d.cts.map +1 -1
- package/dist/agents/transformers/subagent.d.ts +0 -1
- package/dist/agents/transformers/subagent.d.ts.map +1 -1
- package/dist/agents/transformers/subagent.js.map +1 -1
- package/dist/agents/transformers/tool-call.cjs +0 -1
- package/dist/agents/transformers/tool-call.cjs.map +1 -1
- package/dist/agents/transformers/tool-call.d.cts +0 -1
- package/dist/agents/transformers/tool-call.d.cts.map +1 -1
- package/dist/agents/transformers/tool-call.d.ts +0 -1
- package/dist/agents/transformers/tool-call.d.ts.map +1 -1
- package/dist/agents/transformers/tool-call.js.map +1 -1
- package/dist/agents/transformers/types.d.cts +3 -3
- package/dist/agents/transformers/types.d.cts.map +1 -1
- package/dist/agents/transformers/types.d.ts +3 -3
- package/dist/agents/transformers/types.d.ts.map +1 -1
- package/dist/agents/types.d.cts +1 -2
- package/dist/agents/types.d.cts.map +1 -1
- package/dist/agents/types.d.ts +1 -2
- package/dist/agents/types.d.ts.map +1 -1
- package/dist/agents/utils.cjs +0 -1
- package/dist/agents/utils.cjs.map +1 -1
- package/dist/agents/utils.js.map +1 -1
- package/dist/agents/withAgentName.cjs +0 -1
- package/dist/agents/withAgentName.cjs.map +1 -1
- package/dist/agents/withAgentName.js.map +1 -1
- package/dist/browser.d.cts +2 -1
- package/dist/browser.d.ts +2 -1
- package/dist/chat_models/universal.cjs.map +1 -1
- package/dist/chat_models/universal.d.cts +0 -1
- package/dist/chat_models/universal.d.cts.map +1 -1
- package/dist/chat_models/universal.d.ts +0 -1
- package/dist/chat_models/universal.d.ts.map +1 -1
- package/dist/chat_models/universal.js.map +1 -1
- package/dist/hub/base.cjs +0 -1
- package/dist/hub/base.cjs.map +1 -1
- package/dist/hub/base.d.cts +1 -0
- package/dist/hub/base.d.cts.map +1 -1
- package/dist/hub/base.d.ts +1 -0
- package/dist/hub/base.d.ts.map +1 -1
- package/dist/hub/base.js.map +1 -1
- package/dist/hub/index.cjs.map +1 -1
- package/dist/hub/index.d.cts +0 -1
- package/dist/hub/index.d.cts.map +1 -1
- package/dist/hub/index.d.ts +0 -1
- package/dist/hub/index.d.ts.map +1 -1
- package/dist/hub/index.js.map +1 -1
- package/dist/hub/node.cjs.map +1 -1
- package/dist/hub/node.d.cts +0 -1
- package/dist/hub/node.d.cts.map +1 -1
- package/dist/hub/node.d.ts +0 -1
- package/dist/hub/node.d.ts.map +1 -1
- package/dist/hub/node.js.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/load/import_constants.cjs.map +1 -1
- package/dist/load/import_constants.js.map +1 -1
- package/dist/load/import_map.cjs +56 -0
- package/dist/load/import_map.cjs.map +1 -1
- package/dist/load/import_map.js +1 -1
- package/dist/load/import_map.js.map +1 -1
- package/dist/load/import_type.d.cts.map +1 -1
- package/dist/load/import_type.d.ts.map +1 -1
- package/dist/load/index.cjs +0 -1
- package/dist/load/index.cjs.map +1 -1
- package/dist/load/index.d.cts +0 -1
- package/dist/load/index.d.cts.map +1 -1
- package/dist/load/index.d.ts +0 -1
- package/dist/load/index.d.ts.map +1 -1
- package/dist/load/index.js.map +1 -1
- package/dist/storage/encoder_backed.cjs.map +1 -1
- package/dist/storage/encoder_backed.d.cts +0 -1
- package/dist/storage/encoder_backed.d.cts.map +1 -1
- package/dist/storage/encoder_backed.d.ts +0 -1
- package/dist/storage/encoder_backed.d.ts.map +1 -1
- package/dist/storage/encoder_backed.js.map +1 -1
- package/dist/storage/file_system.cjs +4 -3
- package/dist/storage/file_system.cjs.map +1 -1
- package/dist/storage/file_system.d.cts +20 -1
- package/dist/storage/file_system.d.cts.map +1 -1
- package/dist/storage/file_system.d.ts +20 -1
- package/dist/storage/file_system.d.ts.map +1 -1
- package/dist/storage/file_system.js +4 -3
- package/dist/storage/file_system.js.map +1 -1
- package/dist/tools/headless.cjs +0 -1
- package/dist/tools/headless.cjs.map +1 -1
- package/dist/tools/headless.d.cts +5 -3
- package/dist/tools/headless.d.cts.map +1 -1
- package/dist/tools/headless.d.ts +5 -3
- package/dist/tools/headless.d.ts.map +1 -1
- package/dist/tools/headless.js.map +1 -1
- package/package.json +14 -14
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promptCaching.js","names":[],"sources":["../../../../../src/agents/middleware/provider/aws/promptCaching.ts"],"sourcesContent":["import { z } from \"zod/v3\";\nimport { InferInteropZodInput } from \"@langchain/core/utils/types\";\n\nimport type { ConfigurableModel } from \"../../../../chat_models/universal.js\";\nimport { createMiddleware } from \"../../../middleware.js\";\n\nconst DEFAULT_ENABLE_CACHING = true;\nconst DEFAULT_TTL = \"5m\";\nconst DEFAULT_MIN_MESSAGES_TO_CACHE = 1;\nconst DEFAULT_UNSUPPORTED_MODEL_BEHAVIOR = \"warn\";\n\nconst contextSchema = z.object({\n /**\n * Whether to enable prompt caching.\n * @default true\n */\n enableCaching: z.boolean().optional(),\n /**\n * The time-to-live for the cached prompt.\n * @default \"5m\"\n */\n ttl: z.enum([\"5m\", \"1h\"]).optional(),\n /**\n * The minimum number of messages required before caching is applied.\n * @default 1\n */\n minMessagesToCache: z.number().optional(),\n /**\n * The behavior to take when an unsupported model is used.\n * - \"ignore\" will ignore the unsupported model and continue without caching.\n * - \"warn\" will warn the user and continue without caching.\n * - \"raise\" will raise an error and stop the agent.\n * @default \"warn\"\n */\n unsupportedModelBehavior: z.enum([\"ignore\", \"warn\", \"raise\"]).optional(),\n});\nexport type BedrockConversePromptCachingMiddlewareConfig = Partial<\n InferInteropZodInput<typeof contextSchema>\n>;\n\nclass BedrockPromptCachingMiddlewareError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"BedrockPromptCachingMiddlewareError\";\n }\n}\n\n/**\n * Creates a prompt caching middleware for AWS Bedrock Converse models to optimize API usage.\n *\n * This middleware automatically enables Bedrock's prompt caching when using AWS Bedrock Converse\n * models. This can significantly reduce costs for applications with repetitive prompts, long\n * system messages, or extensive conversation histories.\n *\n * ## How It Works\n *\n * The middleware intercepts model requests and sets a cache control signal that\n * `ChatBedrockConverse` translates into Bedrock `cachePoint` breakpoints. Cache points are\n * inserted after the system prompt, after the tool definitions, and after the final message, so\n * the stable prefix of each request is cached. On subsequent requests with a matching prefix, the\n * cached representations are reused, skipping redundant token processing. Exact placement varies\n * by model (e.g. Amazon Nova models cache fewer breakpoints and ignore the `\"1h\"` TTL).\n *\n * ## Benefits\n *\n * - **Cost Reduction**: Avoid reprocessing the same tokens repeatedly\n * - **Lower Latency**: Cached prompts are processed faster as embeddings are pre-computed\n * - **Better Scalability**: Reduced computational load enables handling more requests\n * - **Consistent Performance**: Stable response times for repetitive queries\n *\n * @param middlewareOptions - Configuration options for the caching behavior\n * @param middlewareOptions.enableCaching - Whether to enable prompt caching (default: `true`)\n * @param middlewareOptions.ttl - Cache time-to-live: `\"5m\"` for 5 minutes or `\"1h\"` for 1 hour (default: `\"5m\"`)\n * @param middlewareOptions.minMessagesToCache - Minimum number of messages required before caching is applied (default: `1`)\n * @param middlewareOptions.unsupportedModelBehavior - The behavior to take when an unsupported model is used (default: `\"warn\"`)\n *\n * @returns A middleware instance that can be passed to `createAgent`\n *\n * @throws {Error} When `unsupportedModelBehavior` is `\"raise\"` and the model is not a\n * cache-capable Bedrock Converse model — either a non-Bedrock provider, or a Bedrock\n * Converse model outside the Anthropic Claude / Amazon Nova families.\n *\n * @example\n * Basic usage with default settings\n * ```typescript\n * import { createAgent } from \"langchain\";\n * import { bedrockPromptCachingMiddleware } from \"langchain\";\n *\n * const agent = createAgent({\n * model: \"bedrock:anthropic.claude-haiku-4-5-20251001-v1:0\",\n * middleware: [\n * bedrockPromptCachingMiddleware()\n * ]\n * });\n * ```\n *\n * @example\n * Custom configuration for longer conversations\n * ```typescript\n * const cachingMiddleware = bedrockPromptCachingMiddleware({\n * ttl: \"1h\", // Cache for 1 hour instead of default 5 minutes\n * minMessagesToCache: 5 // Only cache after 5 messages\n * });\n *\n * const agent = createAgent({\n * model: \"bedrock:anthropic.claude-haiku-4-5-20251001-v1:0\",\n * systemPrompt: \"You are a helpful assistant with deep knowledge of...\", // Long system prompt\n * middleware: [cachingMiddleware]\n * });\n * ```\n *\n * @example\n * Conditional caching based on runtime context\n * ```typescript\n * const agent = createAgent({\n * model: \"bedrock:anthropic.claude-haiku-4-5-20251001-v1:0\",\n * middleware: [\n * bedrockPromptCachingMiddleware({\n * enableCaching: true,\n * ttl: \"5m\"\n * })\n * ]\n * });\n *\n * // Disable caching for specific requests\n * await agent.invoke(\n * { messages: [new HumanMessage(\"Process this without caching\")] },\n * {\n * configurable: {\n * middleware_context: { enableCaching: false }\n * }\n * }\n * );\n * ```\n *\n * @example\n * Optimal setup for customer support chatbot\n * ```typescript\n * const supportAgent = createAgent({\n * model: \"bedrock:anthropic.claude-haiku-4-5-20251001-v1:0\",\n * systemPrompt: `You are a customer support agent for ACME Corp.\n *\n * Company policies:\n * - Always be polite and professional\n * - Refer to knowledge base for product information\n * - Escalate billing issues to human agents\n * ... (extensive policies and guidelines)\n * `,\n * tools: [searchKnowledgeBase, createTicket, checkOrderStatus],\n * middleware: [\n * bedrockPromptCachingMiddleware({\n * ttl: \"1h\", // Long TTL for stable system prompt\n * minMessagesToCache: 1 // Cache immediately due to large system prompt\n * })\n * ]\n * });\n * ```\n *\n * @remarks\n * - **Bedrock Converse Only**: This middleware only applies caching to AWS Bedrock Converse models. Other providers are handled per `unsupportedModelBehavior`\n * - **Supported Families**: Bedrock prompt caching is only available on the **Anthropic Claude** and **Amazon Nova** model families. Other Bedrock Converse models (e.g. Mistral, Cohere, Meta) reject cache points at request time, so they are treated as unsupported and routed through `unsupportedModelBehavior`\n * - **Automatic Application**: Caching is applied automatically when the message count reaches `minMessagesToCache`\n * - **TTL Options**: Only supports \"5m\" (5 minutes) and \"1h\" (1 hour) as TTL values; actual support varies by model\n * - **Best Use Cases**: Long system prompts, multi-turn conversations, repetitive queries, RAG applications\n *\n * @see {@link createAgent} for agent creation\n * @see {@link https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html} AWS Bedrock prompt caching documentation\n * @public\n */\nexport function bedrockPromptCachingMiddleware(\n middlewareOptions?: BedrockConversePromptCachingMiddlewareConfig\n) {\n return createMiddleware({\n name: \"BedrockPromptCachingMiddleware\",\n contextSchema,\n wrapModelCall: (request, handler) => {\n const enableCaching =\n request.runtime.context.enableCaching ??\n middlewareOptions?.enableCaching ??\n DEFAULT_ENABLE_CACHING;\n const ttl =\n request.runtime.context.ttl ?? middlewareOptions?.ttl ?? DEFAULT_TTL;\n const minMessagesToCache =\n request.runtime.context.minMessagesToCache ??\n middlewareOptions?.minMessagesToCache ??\n DEFAULT_MIN_MESSAGES_TO_CACHE;\n const unsupportedModelBehavior =\n request.runtime.context.unsupportedModelBehavior ??\n middlewareOptions?.unsupportedModelBehavior ??\n DEFAULT_UNSUPPORTED_MODEL_BEHAVIOR;\n\n // Skip if caching is disabled\n if (!enableCaching || !request.model) {\n return handler(request);\n }\n\n const modelName = request.model.getName();\n const isBedrockConverseModel =\n modelName === \"ChatBedrockConverse\" ||\n (modelName === \"ConfigurableModel\" &&\n ((request.model as ConfigurableModel)._defaultConfig\n ?.modelProvider === \"bedrock\" ||\n (request.model as ConfigurableModel)._defaultConfig\n ?.modelProvider === \"aws\"));\n\n // Resolve the underlying Bedrock model id for cache-capability detection.\n const modelId =\n modelName === \"ConfigurableModel\"\n ? ((request.model as ConfigurableModel)._defaultConfig?.model as\n | string\n | undefined)\n : (request.model as { model?: string }).model;\n\n // Bedrock prompt caching is only supported on the Anthropic Claude and\n // Amazon Nova model families. Other Converse models (Mistral, Cohere,\n // Meta, etc.) reject `cachePoint` blocks with an AccessDeniedException, so\n // they are treated as unsupported.\n const isCacheCapableModel =\n isBedrockConverseModel &&\n typeof modelId === \"string\" &&\n (modelId.toLowerCase().includes(\"anthropic.claude\") ||\n modelId.toLowerCase().includes(\"amazon.nova\"));\n\n if (!isCacheCapableModel) {\n const modelInfo =\n modelName === \"ConfigurableModel\"\n ? `${modelName} (${\n (request.model as ConfigurableModel)._defaultConfig\n ?.modelProvider\n })`\n : modelName;\n\n const baseMessage = isBedrockConverseModel\n ? `Unsupported model '${modelInfo}'. Bedrock prompt caching is only supported on Anthropic Claude and Amazon Nova models`\n : `Unsupported model '${modelInfo}'. Prompt caching requires an AWS Bedrock Converse model`;\n\n if (unsupportedModelBehavior === \"raise\") {\n throw new BedrockPromptCachingMiddlewareError(\n `${baseMessage} (e.g., 'bedrock:anthropic.claude-haiku-4-5-20251001-v1:0').`\n );\n } else if (unsupportedModelBehavior === \"warn\") {\n console.warn(\n `BedrockPromptCachingMiddleware: Skipping caching for ${modelName}. Consider switching to an Anthropic Claude or Amazon Nova model for caching benefits.`\n );\n }\n return handler(request);\n }\n\n const messagesCount =\n request.state.messages.length + (request.systemPrompt ? 1 : 0);\n\n if (messagesCount < minMessagesToCache) {\n return handler(request);\n }\n\n /**\n * The cache_control is applied at the final message formatting layer in\n * ChatBedrockConverse (translated into Converse `cachePoint` blocks).\n *\n * @see https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html\n */\n return handler({\n ...request,\n modelSettings: {\n ...request.modelSettings,\n cache_control: {\n type: \"ephemeral\" as const,\n ttl,\n },\n },\n });\n },\n });\n}\n"],"mappings":";;;AAMA,MAAM,yBAAyB;AAC/B,MAAM,cAAc;AACpB,MAAM,gCAAgC;AACtC,MAAM,qCAAqC;AAE3C,MAAM,gBAAgB,EAAE,OAAO;;;;;CAK7B,eAAe,EAAE,SAAS,CAAC,UAAU;;;;;CAKrC,KAAK,EAAE,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,UAAU;;;;;CAKpC,oBAAoB,EAAE,QAAQ,CAAC,UAAU;;;;;;;;CAQzC,0BAA0B,EAAE,KAAK;EAAC;EAAU;EAAQ;EAAQ,CAAC,CAAC,UAAU;CACzE,CAAC;AAKF,IAAM,sCAAN,cAAkD,MAAM;CACtD,YAAY,SAAiB;AAC3B,QAAM,QAAQ;AACd,OAAK,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8HhB,SAAgB,+BACd,mBACA;AACA,QAAO,iBAAiB;EACtB,MAAM;EACN;EACA,gBAAgB,SAAS,YAAY;GACnC,MAAM,gBACJ,QAAQ,QAAQ,QAAQ,iBACxB,mBAAmB,iBACnB;GACF,MAAM,MACJ,QAAQ,QAAQ,QAAQ,OAAO,mBAAmB,OAAO;GAC3D,MAAM,qBACJ,QAAQ,QAAQ,QAAQ,sBACxB,mBAAmB,sBACnB;GACF,MAAM,2BACJ,QAAQ,QAAQ,QAAQ,4BACxB,mBAAmB,4BACnB;AAGF,OAAI,CAAC,iBAAiB,CAAC,QAAQ,MAC7B,QAAO,QAAQ,QAAQ;GAGzB,MAAM,YAAY,QAAQ,MAAM,SAAS;GACzC,MAAM,yBACJ,cAAc,yBACb,cAAc,wBACX,QAAQ,MAA4B,gBAClC,kBAAkB,aACnB,QAAQ,MAA4B,gBACjC,kBAAkB;GAG5B,MAAM,UACJ,cAAc,sBACR,QAAQ,MAA4B,gBAAgB,QAGrD,QAAQ,MAA6B;AAY5C,OAAI,EALF,0BACA,OAAO,YAAY,aAClB,QAAQ,aAAa,CAAC,SAAS,mBAAmB,IACjD,QAAQ,aAAa,CAAC,SAAS,cAAc,IAEvB;IACxB,MAAM,YACJ,cAAc,sBACV,GAAG,UAAU,IACV,QAAQ,MAA4B,gBACjC,cACL,KACD;IAEN,MAAM,cAAc,yBAChB,sBAAsB,UAAU,0FAChC,sBAAsB,UAAU;AAEpC,QAAI,6BAA6B,QAC/B,OAAM,IAAI,oCACR,GAAG,YAAY,8DAChB;aACQ,6BAA6B,OACtC,SAAQ,KACN,wDAAwD,UAAU,wFACnE;AAEH,WAAO,QAAQ,QAAQ;;AAMzB,OAFE,QAAQ,MAAM,SAAS,UAAU,QAAQ,eAAe,IAAI,KAE1C,mBAClB,QAAO,QAAQ,QAAQ;;;;;;;AASzB,UAAO,QAAQ;IACb,GAAG;IACH,eAAe;KACb,GAAG,QAAQ;KACX,eAAe;MACb,MAAM;MACN;MACD;KACF;IACF,CAAC;;EAEL,CAAC"}
|
|
1
|
+
{"version":3,"file":"promptCaching.js","names":[],"sources":["../../../../../src/agents/middleware/provider/aws/promptCaching.ts"],"sourcesContent":["import { z } from \"zod/v3\";\nimport { InferInteropZodInput } from \"@langchain/core/utils/types\";\n\nimport type { ConfigurableModel } from \"../../../../chat_models/universal.js\";\nimport { createMiddleware } from \"../../../middleware.js\";\n\nconst DEFAULT_ENABLE_CACHING = true;\nconst DEFAULT_TTL = \"5m\";\nconst DEFAULT_MIN_MESSAGES_TO_CACHE = 1;\nconst DEFAULT_UNSUPPORTED_MODEL_BEHAVIOR = \"warn\";\n\nconst contextSchema = z.object({\n /**\n * Whether to enable prompt caching.\n * @default true\n */\n enableCaching: z.boolean().optional(),\n /**\n * The time-to-live for the cached prompt.\n * @default \"5m\"\n */\n ttl: z.enum([\"5m\", \"1h\"]).optional(),\n /**\n * The minimum number of messages required before caching is applied.\n * @default 1\n */\n minMessagesToCache: z.number().optional(),\n /**\n * The behavior to take when an unsupported model is used.\n * - \"ignore\" will ignore the unsupported model and continue without caching.\n * - \"warn\" will warn the user and continue without caching.\n * - \"raise\" will raise an error and stop the agent.\n * @default \"warn\"\n */\n unsupportedModelBehavior: z.enum([\"ignore\", \"warn\", \"raise\"]).optional(),\n});\nexport type BedrockConversePromptCachingMiddlewareConfig = Partial<\n InferInteropZodInput<typeof contextSchema>\n>;\n\nclass BedrockPromptCachingMiddlewareError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"BedrockPromptCachingMiddlewareError\";\n }\n}\n\n/**\n * Creates a prompt caching middleware for AWS Bedrock Converse models to optimize API usage.\n *\n * This middleware automatically enables Bedrock's prompt caching when using AWS Bedrock Converse\n * models. This can significantly reduce costs for applications with repetitive prompts, long\n * system messages, or extensive conversation histories.\n *\n * ## How It Works\n *\n * The middleware intercepts model requests and sets a cache control signal that\n * `ChatBedrockConverse` translates into Bedrock `cachePoint` breakpoints. Cache points are\n * inserted after the system prompt, after the tool definitions, and after the final message, so\n * the stable prefix of each request is cached. On subsequent requests with a matching prefix, the\n * cached representations are reused, skipping redundant token processing. Exact placement varies\n * by model (e.g. Amazon Nova models cache fewer breakpoints and ignore the `\"1h\"` TTL).\n *\n * ## Benefits\n *\n * - **Cost Reduction**: Avoid reprocessing the same tokens repeatedly\n * - **Lower Latency**: Cached prompts are processed faster as embeddings are pre-computed\n * - **Better Scalability**: Reduced computational load enables handling more requests\n * - **Consistent Performance**: Stable response times for repetitive queries\n *\n * @param middlewareOptions - Configuration options for the caching behavior\n * @param middlewareOptions.enableCaching - Whether to enable prompt caching (default: `true`)\n * @param middlewareOptions.ttl - Cache time-to-live: `\"5m\"` for 5 minutes or `\"1h\"` for 1 hour (default: `\"5m\"`)\n * @param middlewareOptions.minMessagesToCache - Minimum number of messages required before caching is applied (default: `1`)\n * @param middlewareOptions.unsupportedModelBehavior - The behavior to take when an unsupported model is used (default: `\"warn\"`)\n *\n * @returns A middleware instance that can be passed to `createAgent`\n *\n * @throws {Error} When `unsupportedModelBehavior` is `\"raise\"` and the model is not a\n * cache-capable Bedrock Converse model — either a non-Bedrock provider, or a Bedrock\n * Converse model outside the Anthropic Claude / Amazon Nova families.\n *\n * @example\n * Basic usage with default settings\n * ```typescript\n * import { createAgent } from \"langchain\";\n * import { bedrockPromptCachingMiddleware } from \"langchain\";\n *\n * const agent = createAgent({\n * model: \"bedrock:anthropic.claude-haiku-4-5-20251001-v1:0\",\n * middleware: [\n * bedrockPromptCachingMiddleware()\n * ]\n * });\n * ```\n *\n * @example\n * Custom configuration for longer conversations\n * ```typescript\n * const cachingMiddleware = bedrockPromptCachingMiddleware({\n * ttl: \"1h\", // Cache for 1 hour instead of default 5 minutes\n * minMessagesToCache: 5 // Only cache after 5 messages\n * });\n *\n * const agent = createAgent({\n * model: \"bedrock:anthropic.claude-haiku-4-5-20251001-v1:0\",\n * systemPrompt: \"You are a helpful assistant with deep knowledge of...\", // Long system prompt\n * middleware: [cachingMiddleware]\n * });\n * ```\n *\n * @example\n * Conditional caching based on runtime context\n * ```typescript\n * const agent = createAgent({\n * model: \"bedrock:anthropic.claude-haiku-4-5-20251001-v1:0\",\n * middleware: [\n * bedrockPromptCachingMiddleware({\n * enableCaching: true,\n * ttl: \"5m\"\n * })\n * ]\n * });\n *\n * // Disable caching for specific requests\n * await agent.invoke(\n * { messages: [new HumanMessage(\"Process this without caching\")] },\n * {\n * configurable: {\n * middleware_context: { enableCaching: false }\n * }\n * }\n * );\n * ```\n *\n * @example\n * Optimal setup for customer support chatbot\n * ```typescript\n * const supportAgent = createAgent({\n * model: \"bedrock:anthropic.claude-haiku-4-5-20251001-v1:0\",\n * systemPrompt: `You are a customer support agent for ACME Corp.\n *\n * Company policies:\n * - Always be polite and professional\n * - Refer to knowledge base for product information\n * - Escalate billing issues to human agents\n * ... (extensive policies and guidelines)\n * `,\n * tools: [searchKnowledgeBase, createTicket, checkOrderStatus],\n * middleware: [\n * bedrockPromptCachingMiddleware({\n * ttl: \"1h\", // Long TTL for stable system prompt\n * minMessagesToCache: 1 // Cache immediately due to large system prompt\n * })\n * ]\n * });\n * ```\n *\n * @remarks\n * - **Bedrock Converse Only**: This middleware only applies caching to AWS Bedrock Converse models. Other providers are handled per `unsupportedModelBehavior`\n * - **Supported Families**: Bedrock prompt caching is only available on the **Anthropic Claude** and **Amazon Nova** model families. Other Bedrock Converse models (e.g. Mistral, Cohere, Meta) reject cache points at request time, so they are treated as unsupported and routed through `unsupportedModelBehavior`\n * - **Automatic Application**: Caching is applied automatically when the message count reaches `minMessagesToCache`\n * - **TTL Options**: Only supports \"5m\" (5 minutes) and \"1h\" (1 hour) as TTL values; actual support varies by model\n * - **Best Use Cases**: Long system prompts, multi-turn conversations, repetitive queries, RAG applications\n *\n * @see {@link createAgent} for agent creation\n * @see {@link https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html} AWS Bedrock prompt caching documentation\n * @public\n */\nexport function bedrockPromptCachingMiddleware(\n middlewareOptions?: BedrockConversePromptCachingMiddlewareConfig\n) {\n return createMiddleware({\n name: \"BedrockPromptCachingMiddleware\",\n contextSchema,\n wrapModelCall: (request, handler) => {\n const enableCaching =\n request.runtime.context.enableCaching ??\n middlewareOptions?.enableCaching ??\n DEFAULT_ENABLE_CACHING;\n const ttl =\n request.runtime.context.ttl ?? middlewareOptions?.ttl ?? DEFAULT_TTL;\n const minMessagesToCache =\n request.runtime.context.minMessagesToCache ??\n middlewareOptions?.minMessagesToCache ??\n DEFAULT_MIN_MESSAGES_TO_CACHE;\n const unsupportedModelBehavior =\n request.runtime.context.unsupportedModelBehavior ??\n middlewareOptions?.unsupportedModelBehavior ??\n DEFAULT_UNSUPPORTED_MODEL_BEHAVIOR;\n\n // Skip if caching is disabled\n if (!enableCaching || !request.model) {\n return handler(request);\n }\n\n const modelName = request.model.getName();\n const isBedrockConverseModel =\n modelName === \"ChatBedrockConverse\" ||\n (modelName === \"ConfigurableModel\" &&\n ((request.model as ConfigurableModel)._defaultConfig\n ?.modelProvider === \"bedrock\" ||\n (request.model as ConfigurableModel)._defaultConfig\n ?.modelProvider === \"aws\"));\n\n // Resolve the underlying Bedrock model id for cache-capability detection.\n const modelId =\n modelName === \"ConfigurableModel\"\n ? ((request.model as ConfigurableModel)._defaultConfig?.model as\n | string\n | undefined)\n : (request.model as { model?: string }).model;\n\n // Bedrock prompt caching is only supported on the Anthropic Claude and\n // Amazon Nova model families. Other Converse models (Mistral, Cohere,\n // Meta, etc.) reject `cachePoint` blocks with an AccessDeniedException, so\n // they are treated as unsupported.\n const isCacheCapableModel =\n isBedrockConverseModel &&\n typeof modelId === \"string\" &&\n (modelId.toLowerCase().includes(\"anthropic.claude\") ||\n modelId.toLowerCase().includes(\"amazon.nova\"));\n\n if (!isCacheCapableModel) {\n const modelInfo =\n modelName === \"ConfigurableModel\"\n ? `${modelName} (${\n (request.model as ConfigurableModel)._defaultConfig\n ?.modelProvider\n })`\n : modelName;\n\n const baseMessage = isBedrockConverseModel\n ? `Unsupported model '${modelInfo}'. Bedrock prompt caching is only supported on Anthropic Claude and Amazon Nova models`\n : `Unsupported model '${modelInfo}'. Prompt caching requires an AWS Bedrock Converse model`;\n\n if (unsupportedModelBehavior === \"raise\") {\n throw new BedrockPromptCachingMiddlewareError(\n `${baseMessage} (e.g., 'bedrock:anthropic.claude-haiku-4-5-20251001-v1:0').`\n );\n } else if (unsupportedModelBehavior === \"warn\") {\n console.warn(\n `BedrockPromptCachingMiddleware: Skipping caching for ${modelName}. Consider switching to an Anthropic Claude or Amazon Nova model for caching benefits.`\n );\n }\n return handler(request);\n }\n\n const messagesCount =\n request.state.messages.length + (request.systemPrompt ? 1 : 0);\n\n if (messagesCount < minMessagesToCache) {\n return handler(request);\n }\n\n /**\n * The cache_control is applied at the final message formatting layer in\n * ChatBedrockConverse (translated into Converse `cachePoint` blocks).\n *\n * @see https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html\n */\n return handler({\n ...request,\n modelSettings: {\n ...request.modelSettings,\n cache_control: {\n type: \"ephemeral\" as const,\n ttl,\n },\n },\n });\n },\n });\n}\n"],"mappings":";;;AAMA,MAAM,yBAAyB;AAC/B,MAAM,cAAc;AACpB,MAAM,gCAAgC;AACtC,MAAM,qCAAqC;AAE3C,MAAM,gBAAgB,EAAE,OAAO;;;;;CAK7B,eAAe,EAAE,QAAQ,CAAC,CAAC,SAAS;;;;;CAKpC,KAAK,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,SAAS;;;;;CAKnC,oBAAoB,EAAE,OAAO,CAAC,CAAC,SAAS;;;;;;;;CAQxC,0BAA0B,EAAE,KAAK;EAAC;EAAU;EAAQ;CAAO,CAAC,CAAC,CAAC,SAAS;AACzE,CAAC;AAKD,IAAM,sCAAN,cAAkD,MAAM;CACtD,YAAY,SAAiB;EAC3B,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4HA,SAAgB,+BACd,mBACA;CACA,OAAO,iBAAiB;EACtB,MAAM;EACN;EACA,gBAAgB,SAAS,YAAY;GACnC,MAAM,gBACJ,QAAQ,QAAQ,QAAQ,iBACxB,mBAAmB,iBACnB;GACF,MAAM,MACJ,QAAQ,QAAQ,QAAQ,OAAO,mBAAmB,OAAO;GAC3D,MAAM,qBACJ,QAAQ,QAAQ,QAAQ,sBACxB,mBAAmB,sBACnB;GACF,MAAM,2BACJ,QAAQ,QAAQ,QAAQ,4BACxB,mBAAmB,4BACnB;GAGF,IAAI,CAAC,iBAAiB,CAAC,QAAQ,OAC7B,OAAO,QAAQ,OAAO;GAGxB,MAAM,YAAY,QAAQ,MAAM,QAAQ;GACxC,MAAM,yBACJ,cAAc,yBACb,cAAc,wBACX,QAAQ,MAA4B,gBAClC,kBAAkB,aACnB,QAAQ,MAA4B,gBACjC,kBAAkB;GAG5B,MAAM,UACJ,cAAc,sBACR,QAAQ,MAA4B,gBAAgB,QAGrD,QAAQ,MAA6B;GAY5C,IAAI,EALF,0BACA,OAAO,YAAY,aAClB,QAAQ,YAAY,CAAC,CAAC,SAAS,kBAAkB,KAChD,QAAQ,YAAY,CAAC,CAAC,SAAS,aAAa,KAEtB;IACxB,MAAM,YACJ,cAAc,sBACV,GAAG,UAAU,IACV,QAAQ,MAA4B,gBACjC,cACL,KACD;IAEN,MAAM,cAAc,yBAChB,sBAAsB,UAAU,0FAChC,sBAAsB,UAAU;IAEpC,IAAI,6BAA6B,SAC/B,MAAM,IAAI,oCACR,GAAG,YAAY,6DACjB;SACK,IAAI,6BAA6B,QACtC,QAAQ,KACN,wDAAwD,UAAU,uFACpE;IAEF,OAAO,QAAQ,OAAO;GACxB;GAKA,IAFE,QAAQ,MAAM,SAAS,UAAU,QAAQ,eAAe,IAAI,KAE1C,oBAClB,OAAO,QAAQ,OAAO;;;;;;;GASxB,OAAO,QAAQ;IACb,GAAG;IACH,eAAe;KACb,GAAG,QAAQ;KACX,eAAe;MACb,MAAM;MACN;KACF;IACF;GACF,CAAC;EACH;CACF,CAAC;AACH"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
require("../../../../_virtual/_rolldown/runtime.cjs");
|
|
2
1
|
const require_chat_models_universal = require("../../../../chat_models/universal.cjs");
|
|
3
2
|
const require_middleware = require("../../../middleware.cjs");
|
|
4
3
|
let _langchain_core_messages = require("@langchain/core/messages");
|
|
@@ -288,6 +287,7 @@ function openAIModerationMiddleware(options) {
|
|
|
288
287
|
});
|
|
289
288
|
}
|
|
290
289
|
//#endregion
|
|
290
|
+
exports.OpenAIModerationError = OpenAIModerationError;
|
|
291
291
|
exports.openAIModerationMiddleware = openAIModerationMiddleware;
|
|
292
292
|
|
|
293
293
|
//# sourceMappingURL=moderation.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"moderation.cjs","names":["initChatModel","AIMessage","HumanMessage","ToolMessage","createMiddleware"],"sources":["../../../../../src/agents/middleware/provider/openai/moderation.ts"],"sourcesContent":["import type { BaseMessage } from \"@langchain/core/messages\";\nimport { AIMessage, HumanMessage, ToolMessage } from \"@langchain/core/messages\";\nimport type { BaseChatModel } from \"@langchain/core/language_models/chat_models\";\nimport { BaseLanguageModel } from \"@langchain/core/language_models/base\";\n\nimport { initChatModel } from \"../../../../chat_models/universal.js\";\nimport { createMiddleware } from \"../../../middleware.js\";\nimport type { MiddlewareResult, AgentMiddleware } from \"../../types.js\";\nimport type { AgentBuiltInState } from \"../../../runtime.js\";\n\n/**\n * OpenAI model interface.\n */\ninterface OpenAIModel extends BaseLanguageModel {\n getName: () => string;\n _getClientOptions: () => unknown;\n client: {\n moderations: {\n create: (\n input: {\n input: string | string[];\n model: string;\n },\n options?: unknown\n ) => Promise<ModerationResponse>;\n };\n };\n}\n\n/**\n * Check if the model is an OpenAI model that supports moderation.\n * @param model - The model to check.\n * @returns Whether the model is an OpenAI model that supports moderation.\n */\nfunction isOpenAIModel(model: unknown): model is OpenAIModel {\n if (\n !model ||\n typeof model !== \"object\" ||\n model === null ||\n !(\"client\" in model) ||\n !(\"_getClientOptions\" in model) ||\n typeof model._getClientOptions !== \"function\"\n ) {\n return false;\n }\n\n /**\n * client may not yet be initialized, so we need to check if the model has a _getClientOptions method.\n */\n model._getClientOptions();\n return (\n typeof model.client === \"object\" &&\n model.client !== null &&\n \"moderations\" in model.client &&\n typeof model.client.moderations === \"object\" &&\n model.client.moderations !== null &&\n \"create\" in model.client.moderations &&\n typeof model.client.moderations.create === \"function\"\n );\n}\n\n/**\n * Stage where a violation occurred.\n */\nexport type ViolationStage = \"input\" | \"output\" | \"tool\";\n\n/**\n * Default template for violation messages.\n */\nconst DEFAULT_VIOLATION_TEMPLATE =\n \"I'm sorry, but I can't comply with that request. It was flagged for {categories}.\";\n\n/**\n * Result of moderation.\n * @see https://platform.openai.com/docs/api-reference/moderations/object\n */\ninterface ModerationResult {\n flagged: boolean;\n categories: Record<string, boolean>;\n category_scores: Record<string, number>;\n category_applied_input_types: Record<string, string[]>;\n}\n\n/**\n * Moderation response.\n * @see https://platform.openai.com/docs/api-reference/moderations/create\n */\ninterface ModerationResponse {\n id: string;\n model: string;\n results: ModerationResult[];\n}\n\ntype ModerationModel =\n | \"omni-moderation-latest\"\n | \"omni-moderation-2024-09-26\"\n | \"text-moderation-latest\"\n | \"text-moderation-stable\";\n\n/**\n * Error raised when OpenAI flags content and `exitBehavior` is set to `\"error\"`.\n */\nexport class OpenAIModerationError extends Error {\n content: string;\n stage: ViolationStage;\n result: ModerationResult;\n originalMessage: string;\n\n constructor({\n content,\n stage,\n result,\n message,\n }: {\n content: string;\n stage: ViolationStage;\n result: ModerationResult;\n message: string;\n }) {\n super(message);\n this.name = \"OpenAIModerationError\";\n this.content = content;\n this.stage = stage;\n this.result = result;\n this.originalMessage = message;\n }\n}\n\n/**\n * Options for configuring the OpenAI Moderation middleware.\n */\nexport interface OpenAIModerationMiddlewareOptions {\n /**\n * OpenAI model to use for moderation. Can be either a model name or a BaseChatModel instance.\n * @example\n * ```ts\n * const model = new ChatOpenAI({ model: \"gpt-4o-mini\" });\n * const middleware = openAIModerationMiddleware({ model });\n * const agent = createAgent({\n * model,\n * middleware: [middleware],\n * });\n * ```\n * @example\n * ```ts\n * const middleware = openAIModerationMiddleware({ model: \"gpt-4o-mini\" });\n * const agent = createAgent({\n * model: \"gpt-5\",\n * middleware: [middleware],\n * });\n * ```\n */\n model: string | BaseChatModel;\n\n /**\n * Moderation model to use.\n * @default \"omni-moderation-latest\"\n */\n moderationModel?: ModerationModel;\n\n /**\n * Whether to check user input messages.\n * @default true\n */\n checkInput?: boolean;\n\n /**\n * Whether to check model output messages.\n * @default true\n */\n checkOutput?: boolean;\n\n /**\n * Whether to check tool result messages.\n * @default false\n */\n checkToolResults?: boolean;\n\n /**\n * How to handle violations.\n * - `\"error\"`: Throw an error when content is flagged\n * - `\"end\"`: End the agent execution and return a violation message\n * - `\"replace\"`: Replace the flagged content with a violation message\n * @default \"end\"\n */\n exitBehavior?: \"error\" | \"end\" | \"replace\";\n\n /**\n * Custom template for violation messages.\n * Available placeholders: `{categories}`, `{category_scores}`, `{original_content}`\n */\n violationMessage?: string;\n}\n\n/**\n * Middleware that moderates agent traffic using OpenAI's moderation endpoint.\n *\n * This middleware checks messages for content policy violations at different stages:\n * - Input: User messages before they reach the model\n * - Output: AI model responses\n * - Tool results: Results returned from tool executions\n *\n * @param options - Configuration options for the middleware\n * @param options.model - OpenAI model to use for moderation. Can be either a model name or a BaseChatModel instance.\n * @param options.moderationModel - Moderation model to use.\n * @param options.checkInput - Whether to check user input messages.\n * @param options.checkOutput - Whether to check model output messages.\n * @param options.checkToolResults - Whether to check tool result messages.\n * @param options.exitBehavior - How to handle violations.\n * @param options.violationMessage - Custom template for violation messages.\n * @returns Middleware function that can be used to moderate agent traffic.\n *\n * @example Using model instance\n * ```ts\n * import { createAgent, openAIModerationMiddleware } from \"langchain\";\n *\n * const middleware = openAIModerationMiddleware({\n * checkInput: true,\n * checkOutput: true,\n * exitBehavior: \"end\"\n * });\n *\n * const agent = createAgent({\n * model: \"openai:gpt-4o\",\n * tools: [...],\n * middleware: [middleware],\n * });\n * ```\n *\n * @example Using model name\n * ```ts\n * import { createAgent, openAIModerationMiddleware } from \"langchain\";\n *\n * const middleware = openAIModerationMiddleware({\n * model: \"gpt-4o-mini\",\n * checkInput: true,\n * checkOutput: true,\n * exitBehavior: \"end\"\n * });\n *\n * const agent = createAgent({\n * model: \"openai:gpt-4o\",\n * tools: [...],\n * middleware: [middleware],\n * });\n * ```\n *\n * @example Custom violation message\n * ```ts\n * const middleware = openAIModerationMiddleware({\n * violationMessage: \"Content flagged: {categories}. Scores: {category_scores}\"\n * });\n * ```\n */\nexport function openAIModerationMiddleware(\n options: OpenAIModerationMiddlewareOptions\n): AgentMiddleware {\n const {\n model,\n moderationModel = \"omni-moderation-latest\",\n checkInput = true,\n checkOutput = true,\n checkToolResults = false,\n exitBehavior = \"end\",\n violationMessage,\n } = options;\n\n let openaiModel: OpenAIModel | undefined;\n const initModerationModel = async (): Promise<OpenAIModel> => {\n if (openaiModel) {\n return openaiModel;\n }\n\n const resolvedModel =\n typeof model === \"string\" ? await initChatModel(model) : model;\n\n /**\n * Check if the model is an OpenAI model.\n */\n if (!resolvedModel.getName().includes(\"ChatOpenAI\")) {\n throw new Error(\n `Model must be an OpenAI model to use moderation middleware. Got: ${resolvedModel.getName()}`\n );\n }\n\n /**\n * check if OpenAI model package supports moderation.\n */\n if (!isOpenAIModel(resolvedModel)) {\n throw new Error(\n \"Model must support moderation to use moderation middleware.\"\n );\n }\n\n openaiModel = resolvedModel as unknown as OpenAIModel;\n return openaiModel;\n };\n\n /**\n * Extract text content from a message.\n */\n const extractText = (message: BaseMessage): string | null => {\n if (message.content == null) {\n return null;\n }\n const text = message.text;\n return text || null;\n };\n\n /**\n * Find the last index of a message type in the messages array.\n */\n const findLastIndex = (\n messages: BaseMessage[],\n messageType: typeof AIMessage | typeof HumanMessage | typeof ToolMessage\n ): number | null => {\n for (let idx = messages.length - 1; idx >= 0; idx--) {\n if (messageType.isInstance(messages[idx])) {\n return idx;\n }\n }\n return null;\n };\n\n /**\n * Format violation message from moderation result.\n */\n const formatViolationMessage = (\n content: string,\n result: ModerationResult\n ): string => {\n // Convert categories to array of flagged category names\n const categories: string[] = [];\n const categoriesObj = result.categories as unknown as Record<\n string,\n boolean\n >;\n for (const [name, flagged] of Object.entries(categoriesObj)) {\n if (flagged) {\n categories.push(name.replace(/_/g, \" \"));\n }\n }\n\n const categoryLabel =\n categories.length > 0\n ? categories.join(\", \")\n : \"OpenAI's safety policies\";\n\n const template = violationMessage || DEFAULT_VIOLATION_TEMPLATE;\n const scoresJson = JSON.stringify(\n result.category_scores as unknown as Record<string, number>,\n null,\n 2\n );\n\n try {\n return template\n .replace(\"{categories}\", categoryLabel)\n .replace(\"{category_scores}\", scoresJson)\n .replace(\"{original_content}\", content);\n } catch {\n return template;\n }\n };\n\n function moderateContent(\n input: string | string[],\n params?: { model?: ModerationModel; options?: unknown }\n ): Promise<ModerationResponse> {\n const clientOptions = openaiModel?._getClientOptions?.();\n const moderationModel = params?.model ?? \"omni-moderation-latest\";\n const moderationRequest = {\n input,\n model: moderationModel,\n };\n return openaiModel!.client.moderations.create(\n moderationRequest,\n clientOptions\n );\n }\n\n /**\n * Apply violation handling based on exit behavior.\n */\n const applyViolation = (\n messages: BaseMessage[],\n index: number | null,\n stage: ViolationStage,\n content: string,\n result: ModerationResult\n ): MiddlewareResult<Partial<AgentBuiltInState>> | undefined => {\n const violationText = formatViolationMessage(content, result);\n\n if (exitBehavior === \"error\") {\n throw new OpenAIModerationError({\n content,\n stage,\n result,\n message: violationText,\n });\n }\n\n if (exitBehavior === \"end\") {\n return {\n jumpTo: \"end\",\n messages: [new AIMessage({ content: violationText })],\n };\n }\n\n if (index == null) {\n return undefined;\n }\n\n /**\n * Replace the original message with a new message that contains the violation text.\n */\n const newMessages = [...messages];\n const original = newMessages[index];\n const MessageConstructor = Object.getPrototypeOf(original).constructor;\n newMessages[index] = new MessageConstructor({\n ...original,\n content: violationText,\n });\n\n return { messages: newMessages };\n };\n\n /**\n * Moderate user input messages.\n */\n const moderateUserMessage = async (\n messages: BaseMessage[]\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | null> => {\n const idx = findLastIndex(messages, HumanMessage);\n if (idx == null) {\n return null;\n }\n\n const message = messages[idx];\n const text = extractText(message);\n if (!text) {\n return null;\n }\n\n await initModerationModel();\n const response = await moderateContent(text, {\n model: moderationModel,\n });\n\n const flaggedResult = response.results.find((result) => result.flagged);\n if (!flaggedResult) {\n return null;\n }\n\n return applyViolation(messages, idx, \"input\", text, flaggedResult);\n };\n\n /**\n * Moderate tool result messages.\n */\n const moderateToolMessages = async (\n messages: BaseMessage[]\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | null> => {\n const lastAiIdx = findLastIndex(messages, AIMessage);\n if (lastAiIdx == null) {\n return null;\n }\n\n const working = [...messages];\n let modified = false;\n\n for (let idx = lastAiIdx + 1; idx < working.length; idx++) {\n const msg = working[idx];\n if (!ToolMessage.isInstance(msg)) {\n continue;\n }\n\n const text = extractText(msg);\n if (!text) {\n continue;\n }\n\n await initModerationModel();\n const response = await moderateContent(text, {\n model: moderationModel,\n });\n const flaggedResult = response.results.find((result) => result.flagged);\n if (!flaggedResult) {\n continue;\n }\n\n const action = applyViolation(working, idx, \"tool\", text, flaggedResult);\n if (action) {\n if (\"jumpTo\" in action) {\n return action;\n }\n if (\"messages\" in action) {\n working.splice(\n 0,\n working.length,\n ...(action.messages as BaseMessage[])\n );\n modified = true;\n }\n }\n }\n\n if (modified) {\n return { messages: working };\n }\n\n return null;\n };\n\n /**\n * Moderate model output messages.\n */\n const moderateOutput = async (\n messages: BaseMessage[]\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | null> => {\n const lastAiIdx = findLastIndex(messages, AIMessage);\n if (lastAiIdx == null) {\n return null;\n }\n\n const aiMessage = messages[lastAiIdx];\n const text = extractText(aiMessage);\n if (!text) {\n return null;\n }\n\n await initModerationModel();\n const response = await moderateContent(text, {\n model: moderationModel,\n });\n const flaggedResult = response.results.find((result) => result.flagged);\n if (!flaggedResult) {\n return null;\n }\n\n return applyViolation(messages, lastAiIdx, \"output\", text, flaggedResult);\n };\n\n /**\n * Moderate inputs (user messages and tool results) before model call.\n */\n const moderateInputs = async (\n messages: BaseMessage[]\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | null> => {\n const working = [...messages];\n let modified = false;\n\n if (checkToolResults) {\n const action = await moderateToolMessages(working);\n if (action) {\n if (\"jumpTo\" in action) {\n return action;\n }\n if (\"messages\" in action) {\n working.splice(\n 0,\n working.length,\n ...(action.messages as BaseMessage[])\n );\n modified = true;\n }\n }\n }\n\n if (checkInput) {\n const action = await moderateUserMessage(working);\n if (action) {\n if (\"jumpTo\" in action) {\n return action;\n }\n if (\"messages\" in action) {\n working.splice(\n 0,\n working.length,\n ...(action.messages as BaseMessage[])\n );\n modified = true;\n }\n }\n }\n\n if (modified) {\n return { messages: working };\n }\n\n return null;\n };\n\n return createMiddleware({\n name: \"OpenAIModerationMiddleware\",\n beforeModel: {\n hook: async (\n state\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | undefined> => {\n if (!checkInput && !checkToolResults) {\n return undefined;\n }\n\n const messages = state.messages || [];\n if (messages.length === 0) {\n return undefined;\n }\n\n return (await moderateInputs(messages)) ?? undefined;\n },\n canJumpTo: [\"end\"],\n },\n afterModel: {\n hook: async (\n state\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | undefined> => {\n if (!checkOutput) {\n return undefined;\n }\n\n const messages = state.messages || [];\n if (messages.length === 0) {\n return undefined;\n }\n\n return (await moderateOutput(messages)) ?? undefined;\n },\n canJumpTo: [\"end\"],\n },\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"moderation.cjs","names":["initChatModel","AIMessage","HumanMessage","ToolMessage","createMiddleware"],"sources":["../../../../../src/agents/middleware/provider/openai/moderation.ts"],"sourcesContent":["import type { BaseMessage } from \"@langchain/core/messages\";\nimport { AIMessage, HumanMessage, ToolMessage } from \"@langchain/core/messages\";\nimport type { BaseChatModel } from \"@langchain/core/language_models/chat_models\";\nimport { BaseLanguageModel } from \"@langchain/core/language_models/base\";\n\nimport { initChatModel } from \"../../../../chat_models/universal.js\";\nimport { createMiddleware } from \"../../../middleware.js\";\nimport type { MiddlewareResult, AgentMiddleware } from \"../../types.js\";\nimport type { AgentBuiltInState } from \"../../../runtime.js\";\n\n/**\n * OpenAI model interface.\n */\ninterface OpenAIModel extends BaseLanguageModel {\n getName: () => string;\n _getClientOptions: () => unknown;\n client: {\n moderations: {\n create: (\n input: {\n input: string | string[];\n model: string;\n },\n options?: unknown\n ) => Promise<ModerationResponse>;\n };\n };\n}\n\n/**\n * Check if the model is an OpenAI model that supports moderation.\n * @param model - The model to check.\n * @returns Whether the model is an OpenAI model that supports moderation.\n */\nfunction isOpenAIModel(model: unknown): model is OpenAIModel {\n if (\n !model ||\n typeof model !== \"object\" ||\n model === null ||\n !(\"client\" in model) ||\n !(\"_getClientOptions\" in model) ||\n typeof model._getClientOptions !== \"function\"\n ) {\n return false;\n }\n\n /**\n * client may not yet be initialized, so we need to check if the model has a _getClientOptions method.\n */\n model._getClientOptions();\n return (\n typeof model.client === \"object\" &&\n model.client !== null &&\n \"moderations\" in model.client &&\n typeof model.client.moderations === \"object\" &&\n model.client.moderations !== null &&\n \"create\" in model.client.moderations &&\n typeof model.client.moderations.create === \"function\"\n );\n}\n\n/**\n * Stage where a violation occurred.\n */\nexport type ViolationStage = \"input\" | \"output\" | \"tool\";\n\n/**\n * Default template for violation messages.\n */\nconst DEFAULT_VIOLATION_TEMPLATE =\n \"I'm sorry, but I can't comply with that request. It was flagged for {categories}.\";\n\n/**\n * Result of moderation.\n * @see https://platform.openai.com/docs/api-reference/moderations/object\n */\ninterface ModerationResult {\n flagged: boolean;\n categories: Record<string, boolean>;\n category_scores: Record<string, number>;\n category_applied_input_types: Record<string, string[]>;\n}\n\n/**\n * Moderation response.\n * @see https://platform.openai.com/docs/api-reference/moderations/create\n */\ninterface ModerationResponse {\n id: string;\n model: string;\n results: ModerationResult[];\n}\n\ntype ModerationModel =\n | \"omni-moderation-latest\"\n | \"omni-moderation-2024-09-26\"\n | \"text-moderation-latest\"\n | \"text-moderation-stable\";\n\n/**\n * Error raised when OpenAI flags content and `exitBehavior` is set to `\"error\"`.\n */\nexport class OpenAIModerationError extends Error {\n content: string;\n stage: ViolationStage;\n result: ModerationResult;\n originalMessage: string;\n\n constructor({\n content,\n stage,\n result,\n message,\n }: {\n content: string;\n stage: ViolationStage;\n result: ModerationResult;\n message: string;\n }) {\n super(message);\n this.name = \"OpenAIModerationError\";\n this.content = content;\n this.stage = stage;\n this.result = result;\n this.originalMessage = message;\n }\n}\n\n/**\n * Options for configuring the OpenAI Moderation middleware.\n */\nexport interface OpenAIModerationMiddlewareOptions {\n /**\n * OpenAI model to use for moderation. Can be either a model name or a BaseChatModel instance.\n * @example\n * ```ts\n * const model = new ChatOpenAI({ model: \"gpt-4o-mini\" });\n * const middleware = openAIModerationMiddleware({ model });\n * const agent = createAgent({\n * model,\n * middleware: [middleware],\n * });\n * ```\n * @example\n * ```ts\n * const middleware = openAIModerationMiddleware({ model: \"gpt-4o-mini\" });\n * const agent = createAgent({\n * model: \"gpt-5\",\n * middleware: [middleware],\n * });\n * ```\n */\n model: string | BaseChatModel;\n\n /**\n * Moderation model to use.\n * @default \"omni-moderation-latest\"\n */\n moderationModel?: ModerationModel;\n\n /**\n * Whether to check user input messages.\n * @default true\n */\n checkInput?: boolean;\n\n /**\n * Whether to check model output messages.\n * @default true\n */\n checkOutput?: boolean;\n\n /**\n * Whether to check tool result messages.\n * @default false\n */\n checkToolResults?: boolean;\n\n /**\n * How to handle violations.\n * - `\"error\"`: Throw an error when content is flagged\n * - `\"end\"`: End the agent execution and return a violation message\n * - `\"replace\"`: Replace the flagged content with a violation message\n * @default \"end\"\n */\n exitBehavior?: \"error\" | \"end\" | \"replace\";\n\n /**\n * Custom template for violation messages.\n * Available placeholders: `{categories}`, `{category_scores}`, `{original_content}`\n */\n violationMessage?: string;\n}\n\n/**\n * Middleware that moderates agent traffic using OpenAI's moderation endpoint.\n *\n * This middleware checks messages for content policy violations at different stages:\n * - Input: User messages before they reach the model\n * - Output: AI model responses\n * - Tool results: Results returned from tool executions\n *\n * @param options - Configuration options for the middleware\n * @param options.model - OpenAI model to use for moderation. Can be either a model name or a BaseChatModel instance.\n * @param options.moderationModel - Moderation model to use.\n * @param options.checkInput - Whether to check user input messages.\n * @param options.checkOutput - Whether to check model output messages.\n * @param options.checkToolResults - Whether to check tool result messages.\n * @param options.exitBehavior - How to handle violations.\n * @param options.violationMessage - Custom template for violation messages.\n * @returns Middleware function that can be used to moderate agent traffic.\n *\n * @example Using model instance\n * ```ts\n * import { createAgent, openAIModerationMiddleware } from \"langchain\";\n *\n * const middleware = openAIModerationMiddleware({\n * checkInput: true,\n * checkOutput: true,\n * exitBehavior: \"end\"\n * });\n *\n * const agent = createAgent({\n * model: \"openai:gpt-4o\",\n * tools: [...],\n * middleware: [middleware],\n * });\n * ```\n *\n * @example Using model name\n * ```ts\n * import { createAgent, openAIModerationMiddleware } from \"langchain\";\n *\n * const middleware = openAIModerationMiddleware({\n * model: \"gpt-4o-mini\",\n * checkInput: true,\n * checkOutput: true,\n * exitBehavior: \"end\"\n * });\n *\n * const agent = createAgent({\n * model: \"openai:gpt-4o\",\n * tools: [...],\n * middleware: [middleware],\n * });\n * ```\n *\n * @example Custom violation message\n * ```ts\n * const middleware = openAIModerationMiddleware({\n * violationMessage: \"Content flagged: {categories}. Scores: {category_scores}\"\n * });\n * ```\n */\nexport function openAIModerationMiddleware(\n options: OpenAIModerationMiddlewareOptions\n): AgentMiddleware {\n const {\n model,\n moderationModel = \"omni-moderation-latest\",\n checkInput = true,\n checkOutput = true,\n checkToolResults = false,\n exitBehavior = \"end\",\n violationMessage,\n } = options;\n\n let openaiModel: OpenAIModel | undefined;\n const initModerationModel = async (): Promise<OpenAIModel> => {\n if (openaiModel) {\n return openaiModel;\n }\n\n const resolvedModel =\n typeof model === \"string\" ? await initChatModel(model) : model;\n\n /**\n * Check if the model is an OpenAI model.\n */\n if (!resolvedModel.getName().includes(\"ChatOpenAI\")) {\n throw new Error(\n `Model must be an OpenAI model to use moderation middleware. Got: ${resolvedModel.getName()}`\n );\n }\n\n /**\n * check if OpenAI model package supports moderation.\n */\n if (!isOpenAIModel(resolvedModel)) {\n throw new Error(\n \"Model must support moderation to use moderation middleware.\"\n );\n }\n\n openaiModel = resolvedModel as unknown as OpenAIModel;\n return openaiModel;\n };\n\n /**\n * Extract text content from a message.\n */\n const extractText = (message: BaseMessage): string | null => {\n if (message.content == null) {\n return null;\n }\n const text = message.text;\n return text || null;\n };\n\n /**\n * Find the last index of a message type in the messages array.\n */\n const findLastIndex = (\n messages: BaseMessage[],\n messageType: typeof AIMessage | typeof HumanMessage | typeof ToolMessage\n ): number | null => {\n for (let idx = messages.length - 1; idx >= 0; idx--) {\n if (messageType.isInstance(messages[idx])) {\n return idx;\n }\n }\n return null;\n };\n\n /**\n * Format violation message from moderation result.\n */\n const formatViolationMessage = (\n content: string,\n result: ModerationResult\n ): string => {\n // Convert categories to array of flagged category names\n const categories: string[] = [];\n const categoriesObj = result.categories as unknown as Record<\n string,\n boolean\n >;\n for (const [name, flagged] of Object.entries(categoriesObj)) {\n if (flagged) {\n categories.push(name.replace(/_/g, \" \"));\n }\n }\n\n const categoryLabel =\n categories.length > 0\n ? categories.join(\", \")\n : \"OpenAI's safety policies\";\n\n const template = violationMessage || DEFAULT_VIOLATION_TEMPLATE;\n const scoresJson = JSON.stringify(\n result.category_scores as unknown as Record<string, number>,\n null,\n 2\n );\n\n try {\n return template\n .replace(\"{categories}\", categoryLabel)\n .replace(\"{category_scores}\", scoresJson)\n .replace(\"{original_content}\", content);\n } catch {\n return template;\n }\n };\n\n function moderateContent(\n input: string | string[],\n params?: { model?: ModerationModel; options?: unknown }\n ): Promise<ModerationResponse> {\n const clientOptions = openaiModel?._getClientOptions?.();\n const moderationModel = params?.model ?? \"omni-moderation-latest\";\n const moderationRequest = {\n input,\n model: moderationModel,\n };\n return openaiModel!.client.moderations.create(\n moderationRequest,\n clientOptions\n );\n }\n\n /**\n * Apply violation handling based on exit behavior.\n */\n const applyViolation = (\n messages: BaseMessage[],\n index: number | null,\n stage: ViolationStage,\n content: string,\n result: ModerationResult\n ): MiddlewareResult<Partial<AgentBuiltInState>> | undefined => {\n const violationText = formatViolationMessage(content, result);\n\n if (exitBehavior === \"error\") {\n throw new OpenAIModerationError({\n content,\n stage,\n result,\n message: violationText,\n });\n }\n\n if (exitBehavior === \"end\") {\n return {\n jumpTo: \"end\",\n messages: [new AIMessage({ content: violationText })],\n };\n }\n\n if (index == null) {\n return undefined;\n }\n\n /**\n * Replace the original message with a new message that contains the violation text.\n */\n const newMessages = [...messages];\n const original = newMessages[index];\n const MessageConstructor = Object.getPrototypeOf(original).constructor;\n newMessages[index] = new MessageConstructor({\n ...original,\n content: violationText,\n });\n\n return { messages: newMessages };\n };\n\n /**\n * Moderate user input messages.\n */\n const moderateUserMessage = async (\n messages: BaseMessage[]\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | null> => {\n const idx = findLastIndex(messages, HumanMessage);\n if (idx == null) {\n return null;\n }\n\n const message = messages[idx];\n const text = extractText(message);\n if (!text) {\n return null;\n }\n\n await initModerationModel();\n const response = await moderateContent(text, {\n model: moderationModel,\n });\n\n const flaggedResult = response.results.find((result) => result.flagged);\n if (!flaggedResult) {\n return null;\n }\n\n return applyViolation(messages, idx, \"input\", text, flaggedResult);\n };\n\n /**\n * Moderate tool result messages.\n */\n const moderateToolMessages = async (\n messages: BaseMessage[]\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | null> => {\n const lastAiIdx = findLastIndex(messages, AIMessage);\n if (lastAiIdx == null) {\n return null;\n }\n\n const working = [...messages];\n let modified = false;\n\n for (let idx = lastAiIdx + 1; idx < working.length; idx++) {\n const msg = working[idx];\n if (!ToolMessage.isInstance(msg)) {\n continue;\n }\n\n const text = extractText(msg);\n if (!text) {\n continue;\n }\n\n await initModerationModel();\n const response = await moderateContent(text, {\n model: moderationModel,\n });\n const flaggedResult = response.results.find((result) => result.flagged);\n if (!flaggedResult) {\n continue;\n }\n\n const action = applyViolation(working, idx, \"tool\", text, flaggedResult);\n if (action) {\n if (\"jumpTo\" in action) {\n return action;\n }\n if (\"messages\" in action) {\n working.splice(\n 0,\n working.length,\n ...(action.messages as BaseMessage[])\n );\n modified = true;\n }\n }\n }\n\n if (modified) {\n return { messages: working };\n }\n\n return null;\n };\n\n /**\n * Moderate model output messages.\n */\n const moderateOutput = async (\n messages: BaseMessage[]\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | null> => {\n const lastAiIdx = findLastIndex(messages, AIMessage);\n if (lastAiIdx == null) {\n return null;\n }\n\n const aiMessage = messages[lastAiIdx];\n const text = extractText(aiMessage);\n if (!text) {\n return null;\n }\n\n await initModerationModel();\n const response = await moderateContent(text, {\n model: moderationModel,\n });\n const flaggedResult = response.results.find((result) => result.flagged);\n if (!flaggedResult) {\n return null;\n }\n\n return applyViolation(messages, lastAiIdx, \"output\", text, flaggedResult);\n };\n\n /**\n * Moderate inputs (user messages and tool results) before model call.\n */\n const moderateInputs = async (\n messages: BaseMessage[]\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | null> => {\n const working = [...messages];\n let modified = false;\n\n if (checkToolResults) {\n const action = await moderateToolMessages(working);\n if (action) {\n if (\"jumpTo\" in action) {\n return action;\n }\n if (\"messages\" in action) {\n working.splice(\n 0,\n working.length,\n ...(action.messages as BaseMessage[])\n );\n modified = true;\n }\n }\n }\n\n if (checkInput) {\n const action = await moderateUserMessage(working);\n if (action) {\n if (\"jumpTo\" in action) {\n return action;\n }\n if (\"messages\" in action) {\n working.splice(\n 0,\n working.length,\n ...(action.messages as BaseMessage[])\n );\n modified = true;\n }\n }\n }\n\n if (modified) {\n return { messages: working };\n }\n\n return null;\n };\n\n return createMiddleware({\n name: \"OpenAIModerationMiddleware\",\n beforeModel: {\n hook: async (\n state\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | undefined> => {\n if (!checkInput && !checkToolResults) {\n return undefined;\n }\n\n const messages = state.messages || [];\n if (messages.length === 0) {\n return undefined;\n }\n\n return (await moderateInputs(messages)) ?? undefined;\n },\n canJumpTo: [\"end\"],\n },\n afterModel: {\n hook: async (\n state\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | undefined> => {\n if (!checkOutput) {\n return undefined;\n }\n\n const messages = state.messages || [];\n if (messages.length === 0) {\n return undefined;\n }\n\n return (await moderateOutput(messages)) ?? undefined;\n },\n canJumpTo: [\"end\"],\n },\n });\n}\n"],"mappings":"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"}
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { AgentMiddleware } from "../../types.cjs";
|
|
2
2
|
import { BaseChatModel } from "@langchain/core/language_models/chat_models";
|
|
3
|
-
|
|
4
3
|
//#region src/agents/middleware/provider/openai/moderation.d.ts
|
|
5
4
|
type ModerationModel = "omni-moderation-latest" | "omni-moderation-2024-09-26" | "text-moderation-latest" | "text-moderation-stable";
|
|
6
|
-
/**
|
|
7
|
-
* Error raised when OpenAI flags content and `exitBehavior` is set to `"error"`.
|
|
8
|
-
*/
|
|
9
5
|
/**
|
|
10
6
|
* Options for configuring the OpenAI Moderation middleware.
|
|
11
7
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"moderation.d.cts","names":[],"sources":["../../../../../src/agents/middleware/provider/openai/moderation.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"moderation.d.cts","names":[],"sources":["../../../../../src/agents/middleware/provider/openai/moderation.ts"],"mappings":";;;KA6FK;;;;UAsCY;;;;;;;;;;;;;;;;;;;;;EAqBf,gBAAgB;;;;;EAMhB,kBAAkB;;;;;EAMlB;;;;;EAMA;;;;;EAMA;;;;;;;;EASA;;;;;EAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+Dc,2BACd,SAAS,oCACR"}
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { AgentMiddleware } from "../../types.js";
|
|
2
2
|
import { BaseChatModel } from "@langchain/core/language_models/chat_models";
|
|
3
|
-
|
|
4
3
|
//#region src/agents/middleware/provider/openai/moderation.d.ts
|
|
5
4
|
type ModerationModel = "omni-moderation-latest" | "omni-moderation-2024-09-26" | "text-moderation-latest" | "text-moderation-stable";
|
|
6
|
-
/**
|
|
7
|
-
* Error raised when OpenAI flags content and `exitBehavior` is set to `"error"`.
|
|
8
|
-
*/
|
|
9
5
|
/**
|
|
10
6
|
* Options for configuring the OpenAI Moderation middleware.
|
|
11
7
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"moderation.d.ts","names":[],"sources":["../../../../../src/agents/middleware/provider/openai/moderation.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"moderation.d.ts","names":[],"sources":["../../../../../src/agents/middleware/provider/openai/moderation.ts"],"mappings":";;;KA6FK;;;;UAsCY;;;;;;;;;;;;;;;;;;;;;EAqBf,gBAAgB;;;;;EAMhB,kBAAkB;;;;;EAMlB;;;;;EAMA;;;;;EAMA;;;;;;;;EASA;;;;;EAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+Dc,2BACd,SAAS,oCACR"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"moderation.js","names":[],"sources":["../../../../../src/agents/middleware/provider/openai/moderation.ts"],"sourcesContent":["import type { BaseMessage } from \"@langchain/core/messages\";\nimport { AIMessage, HumanMessage, ToolMessage } from \"@langchain/core/messages\";\nimport type { BaseChatModel } from \"@langchain/core/language_models/chat_models\";\nimport { BaseLanguageModel } from \"@langchain/core/language_models/base\";\n\nimport { initChatModel } from \"../../../../chat_models/universal.js\";\nimport { createMiddleware } from \"../../../middleware.js\";\nimport type { MiddlewareResult, AgentMiddleware } from \"../../types.js\";\nimport type { AgentBuiltInState } from \"../../../runtime.js\";\n\n/**\n * OpenAI model interface.\n */\ninterface OpenAIModel extends BaseLanguageModel {\n getName: () => string;\n _getClientOptions: () => unknown;\n client: {\n moderations: {\n create: (\n input: {\n input: string | string[];\n model: string;\n },\n options?: unknown\n ) => Promise<ModerationResponse>;\n };\n };\n}\n\n/**\n * Check if the model is an OpenAI model that supports moderation.\n * @param model - The model to check.\n * @returns Whether the model is an OpenAI model that supports moderation.\n */\nfunction isOpenAIModel(model: unknown): model is OpenAIModel {\n if (\n !model ||\n typeof model !== \"object\" ||\n model === null ||\n !(\"client\" in model) ||\n !(\"_getClientOptions\" in model) ||\n typeof model._getClientOptions !== \"function\"\n ) {\n return false;\n }\n\n /**\n * client may not yet be initialized, so we need to check if the model has a _getClientOptions method.\n */\n model._getClientOptions();\n return (\n typeof model.client === \"object\" &&\n model.client !== null &&\n \"moderations\" in model.client &&\n typeof model.client.moderations === \"object\" &&\n model.client.moderations !== null &&\n \"create\" in model.client.moderations &&\n typeof model.client.moderations.create === \"function\"\n );\n}\n\n/**\n * Stage where a violation occurred.\n */\nexport type ViolationStage = \"input\" | \"output\" | \"tool\";\n\n/**\n * Default template for violation messages.\n */\nconst DEFAULT_VIOLATION_TEMPLATE =\n \"I'm sorry, but I can't comply with that request. It was flagged for {categories}.\";\n\n/**\n * Result of moderation.\n * @see https://platform.openai.com/docs/api-reference/moderations/object\n */\ninterface ModerationResult {\n flagged: boolean;\n categories: Record<string, boolean>;\n category_scores: Record<string, number>;\n category_applied_input_types: Record<string, string[]>;\n}\n\n/**\n * Moderation response.\n * @see https://platform.openai.com/docs/api-reference/moderations/create\n */\ninterface ModerationResponse {\n id: string;\n model: string;\n results: ModerationResult[];\n}\n\ntype ModerationModel =\n | \"omni-moderation-latest\"\n | \"omni-moderation-2024-09-26\"\n | \"text-moderation-latest\"\n | \"text-moderation-stable\";\n\n/**\n * Error raised when OpenAI flags content and `exitBehavior` is set to `\"error\"`.\n */\nexport class OpenAIModerationError extends Error {\n content: string;\n stage: ViolationStage;\n result: ModerationResult;\n originalMessage: string;\n\n constructor({\n content,\n stage,\n result,\n message,\n }: {\n content: string;\n stage: ViolationStage;\n result: ModerationResult;\n message: string;\n }) {\n super(message);\n this.name = \"OpenAIModerationError\";\n this.content = content;\n this.stage = stage;\n this.result = result;\n this.originalMessage = message;\n }\n}\n\n/**\n * Options for configuring the OpenAI Moderation middleware.\n */\nexport interface OpenAIModerationMiddlewareOptions {\n /**\n * OpenAI model to use for moderation. Can be either a model name or a BaseChatModel instance.\n * @example\n * ```ts\n * const model = new ChatOpenAI({ model: \"gpt-4o-mini\" });\n * const middleware = openAIModerationMiddleware({ model });\n * const agent = createAgent({\n * model,\n * middleware: [middleware],\n * });\n * ```\n * @example\n * ```ts\n * const middleware = openAIModerationMiddleware({ model: \"gpt-4o-mini\" });\n * const agent = createAgent({\n * model: \"gpt-5\",\n * middleware: [middleware],\n * });\n * ```\n */\n model: string | BaseChatModel;\n\n /**\n * Moderation model to use.\n * @default \"omni-moderation-latest\"\n */\n moderationModel?: ModerationModel;\n\n /**\n * Whether to check user input messages.\n * @default true\n */\n checkInput?: boolean;\n\n /**\n * Whether to check model output messages.\n * @default true\n */\n checkOutput?: boolean;\n\n /**\n * Whether to check tool result messages.\n * @default false\n */\n checkToolResults?: boolean;\n\n /**\n * How to handle violations.\n * - `\"error\"`: Throw an error when content is flagged\n * - `\"end\"`: End the agent execution and return a violation message\n * - `\"replace\"`: Replace the flagged content with a violation message\n * @default \"end\"\n */\n exitBehavior?: \"error\" | \"end\" | \"replace\";\n\n /**\n * Custom template for violation messages.\n * Available placeholders: `{categories}`, `{category_scores}`, `{original_content}`\n */\n violationMessage?: string;\n}\n\n/**\n * Middleware that moderates agent traffic using OpenAI's moderation endpoint.\n *\n * This middleware checks messages for content policy violations at different stages:\n * - Input: User messages before they reach the model\n * - Output: AI model responses\n * - Tool results: Results returned from tool executions\n *\n * @param options - Configuration options for the middleware\n * @param options.model - OpenAI model to use for moderation. Can be either a model name or a BaseChatModel instance.\n * @param options.moderationModel - Moderation model to use.\n * @param options.checkInput - Whether to check user input messages.\n * @param options.checkOutput - Whether to check model output messages.\n * @param options.checkToolResults - Whether to check tool result messages.\n * @param options.exitBehavior - How to handle violations.\n * @param options.violationMessage - Custom template for violation messages.\n * @returns Middleware function that can be used to moderate agent traffic.\n *\n * @example Using model instance\n * ```ts\n * import { createAgent, openAIModerationMiddleware } from \"langchain\";\n *\n * const middleware = openAIModerationMiddleware({\n * checkInput: true,\n * checkOutput: true,\n * exitBehavior: \"end\"\n * });\n *\n * const agent = createAgent({\n * model: \"openai:gpt-4o\",\n * tools: [...],\n * middleware: [middleware],\n * });\n * ```\n *\n * @example Using model name\n * ```ts\n * import { createAgent, openAIModerationMiddleware } from \"langchain\";\n *\n * const middleware = openAIModerationMiddleware({\n * model: \"gpt-4o-mini\",\n * checkInput: true,\n * checkOutput: true,\n * exitBehavior: \"end\"\n * });\n *\n * const agent = createAgent({\n * model: \"openai:gpt-4o\",\n * tools: [...],\n * middleware: [middleware],\n * });\n * ```\n *\n * @example Custom violation message\n * ```ts\n * const middleware = openAIModerationMiddleware({\n * violationMessage: \"Content flagged: {categories}. Scores: {category_scores}\"\n * });\n * ```\n */\nexport function openAIModerationMiddleware(\n options: OpenAIModerationMiddlewareOptions\n): AgentMiddleware {\n const {\n model,\n moderationModel = \"omni-moderation-latest\",\n checkInput = true,\n checkOutput = true,\n checkToolResults = false,\n exitBehavior = \"end\",\n violationMessage,\n } = options;\n\n let openaiModel: OpenAIModel | undefined;\n const initModerationModel = async (): Promise<OpenAIModel> => {\n if (openaiModel) {\n return openaiModel;\n }\n\n const resolvedModel =\n typeof model === \"string\" ? await initChatModel(model) : model;\n\n /**\n * Check if the model is an OpenAI model.\n */\n if (!resolvedModel.getName().includes(\"ChatOpenAI\")) {\n throw new Error(\n `Model must be an OpenAI model to use moderation middleware. Got: ${resolvedModel.getName()}`\n );\n }\n\n /**\n * check if OpenAI model package supports moderation.\n */\n if (!isOpenAIModel(resolvedModel)) {\n throw new Error(\n \"Model must support moderation to use moderation middleware.\"\n );\n }\n\n openaiModel = resolvedModel as unknown as OpenAIModel;\n return openaiModel;\n };\n\n /**\n * Extract text content from a message.\n */\n const extractText = (message: BaseMessage): string | null => {\n if (message.content == null) {\n return null;\n }\n const text = message.text;\n return text || null;\n };\n\n /**\n * Find the last index of a message type in the messages array.\n */\n const findLastIndex = (\n messages: BaseMessage[],\n messageType: typeof AIMessage | typeof HumanMessage | typeof ToolMessage\n ): number | null => {\n for (let idx = messages.length - 1; idx >= 0; idx--) {\n if (messageType.isInstance(messages[idx])) {\n return idx;\n }\n }\n return null;\n };\n\n /**\n * Format violation message from moderation result.\n */\n const formatViolationMessage = (\n content: string,\n result: ModerationResult\n ): string => {\n // Convert categories to array of flagged category names\n const categories: string[] = [];\n const categoriesObj = result.categories as unknown as Record<\n string,\n boolean\n >;\n for (const [name, flagged] of Object.entries(categoriesObj)) {\n if (flagged) {\n categories.push(name.replace(/_/g, \" \"));\n }\n }\n\n const categoryLabel =\n categories.length > 0\n ? categories.join(\", \")\n : \"OpenAI's safety policies\";\n\n const template = violationMessage || DEFAULT_VIOLATION_TEMPLATE;\n const scoresJson = JSON.stringify(\n result.category_scores as unknown as Record<string, number>,\n null,\n 2\n );\n\n try {\n return template\n .replace(\"{categories}\", categoryLabel)\n .replace(\"{category_scores}\", scoresJson)\n .replace(\"{original_content}\", content);\n } catch {\n return template;\n }\n };\n\n function moderateContent(\n input: string | string[],\n params?: { model?: ModerationModel; options?: unknown }\n ): Promise<ModerationResponse> {\n const clientOptions = openaiModel?._getClientOptions?.();\n const moderationModel = params?.model ?? \"omni-moderation-latest\";\n const moderationRequest = {\n input,\n model: moderationModel,\n };\n return openaiModel!.client.moderations.create(\n moderationRequest,\n clientOptions\n );\n }\n\n /**\n * Apply violation handling based on exit behavior.\n */\n const applyViolation = (\n messages: BaseMessage[],\n index: number | null,\n stage: ViolationStage,\n content: string,\n result: ModerationResult\n ): MiddlewareResult<Partial<AgentBuiltInState>> | undefined => {\n const violationText = formatViolationMessage(content, result);\n\n if (exitBehavior === \"error\") {\n throw new OpenAIModerationError({\n content,\n stage,\n result,\n message: violationText,\n });\n }\n\n if (exitBehavior === \"end\") {\n return {\n jumpTo: \"end\",\n messages: [new AIMessage({ content: violationText })],\n };\n }\n\n if (index == null) {\n return undefined;\n }\n\n /**\n * Replace the original message with a new message that contains the violation text.\n */\n const newMessages = [...messages];\n const original = newMessages[index];\n const MessageConstructor = Object.getPrototypeOf(original).constructor;\n newMessages[index] = new MessageConstructor({\n ...original,\n content: violationText,\n });\n\n return { messages: newMessages };\n };\n\n /**\n * Moderate user input messages.\n */\n const moderateUserMessage = async (\n messages: BaseMessage[]\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | null> => {\n const idx = findLastIndex(messages, HumanMessage);\n if (idx == null) {\n return null;\n }\n\n const message = messages[idx];\n const text = extractText(message);\n if (!text) {\n return null;\n }\n\n await initModerationModel();\n const response = await moderateContent(text, {\n model: moderationModel,\n });\n\n const flaggedResult = response.results.find((result) => result.flagged);\n if (!flaggedResult) {\n return null;\n }\n\n return applyViolation(messages, idx, \"input\", text, flaggedResult);\n };\n\n /**\n * Moderate tool result messages.\n */\n const moderateToolMessages = async (\n messages: BaseMessage[]\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | null> => {\n const lastAiIdx = findLastIndex(messages, AIMessage);\n if (lastAiIdx == null) {\n return null;\n }\n\n const working = [...messages];\n let modified = false;\n\n for (let idx = lastAiIdx + 1; idx < working.length; idx++) {\n const msg = working[idx];\n if (!ToolMessage.isInstance(msg)) {\n continue;\n }\n\n const text = extractText(msg);\n if (!text) {\n continue;\n }\n\n await initModerationModel();\n const response = await moderateContent(text, {\n model: moderationModel,\n });\n const flaggedResult = response.results.find((result) => result.flagged);\n if (!flaggedResult) {\n continue;\n }\n\n const action = applyViolation(working, idx, \"tool\", text, flaggedResult);\n if (action) {\n if (\"jumpTo\" in action) {\n return action;\n }\n if (\"messages\" in action) {\n working.splice(\n 0,\n working.length,\n ...(action.messages as BaseMessage[])\n );\n modified = true;\n }\n }\n }\n\n if (modified) {\n return { messages: working };\n }\n\n return null;\n };\n\n /**\n * Moderate model output messages.\n */\n const moderateOutput = async (\n messages: BaseMessage[]\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | null> => {\n const lastAiIdx = findLastIndex(messages, AIMessage);\n if (lastAiIdx == null) {\n return null;\n }\n\n const aiMessage = messages[lastAiIdx];\n const text = extractText(aiMessage);\n if (!text) {\n return null;\n }\n\n await initModerationModel();\n const response = await moderateContent(text, {\n model: moderationModel,\n });\n const flaggedResult = response.results.find((result) => result.flagged);\n if (!flaggedResult) {\n return null;\n }\n\n return applyViolation(messages, lastAiIdx, \"output\", text, flaggedResult);\n };\n\n /**\n * Moderate inputs (user messages and tool results) before model call.\n */\n const moderateInputs = async (\n messages: BaseMessage[]\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | null> => {\n const working = [...messages];\n let modified = false;\n\n if (checkToolResults) {\n const action = await moderateToolMessages(working);\n if (action) {\n if (\"jumpTo\" in action) {\n return action;\n }\n if (\"messages\" in action) {\n working.splice(\n 0,\n working.length,\n ...(action.messages as BaseMessage[])\n );\n modified = true;\n }\n }\n }\n\n if (checkInput) {\n const action = await moderateUserMessage(working);\n if (action) {\n if (\"jumpTo\" in action) {\n return action;\n }\n if (\"messages\" in action) {\n working.splice(\n 0,\n working.length,\n ...(action.messages as BaseMessage[])\n );\n modified = true;\n }\n }\n }\n\n if (modified) {\n return { messages: working };\n }\n\n return null;\n };\n\n return createMiddleware({\n name: \"OpenAIModerationMiddleware\",\n beforeModel: {\n hook: async (\n state\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | undefined> => {\n if (!checkInput && !checkToolResults) {\n return undefined;\n }\n\n const messages = state.messages || [];\n if (messages.length === 0) {\n return undefined;\n }\n\n return (await moderateInputs(messages)) ?? undefined;\n },\n canJumpTo: [\"end\"],\n },\n afterModel: {\n hook: async (\n state\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | undefined> => {\n if (!checkOutput) {\n return undefined;\n }\n\n const messages = state.messages || [];\n if (messages.length === 0) {\n return undefined;\n }\n\n return (await moderateOutput(messages)) ?? undefined;\n },\n canJumpTo: [\"end\"],\n },\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"moderation.js","names":[],"sources":["../../../../../src/agents/middleware/provider/openai/moderation.ts"],"sourcesContent":["import type { BaseMessage } from \"@langchain/core/messages\";\nimport { AIMessage, HumanMessage, ToolMessage } from \"@langchain/core/messages\";\nimport type { BaseChatModel } from \"@langchain/core/language_models/chat_models\";\nimport { BaseLanguageModel } from \"@langchain/core/language_models/base\";\n\nimport { initChatModel } from \"../../../../chat_models/universal.js\";\nimport { createMiddleware } from \"../../../middleware.js\";\nimport type { MiddlewareResult, AgentMiddleware } from \"../../types.js\";\nimport type { AgentBuiltInState } from \"../../../runtime.js\";\n\n/**\n * OpenAI model interface.\n */\ninterface OpenAIModel extends BaseLanguageModel {\n getName: () => string;\n _getClientOptions: () => unknown;\n client: {\n moderations: {\n create: (\n input: {\n input: string | string[];\n model: string;\n },\n options?: unknown\n ) => Promise<ModerationResponse>;\n };\n };\n}\n\n/**\n * Check if the model is an OpenAI model that supports moderation.\n * @param model - The model to check.\n * @returns Whether the model is an OpenAI model that supports moderation.\n */\nfunction isOpenAIModel(model: unknown): model is OpenAIModel {\n if (\n !model ||\n typeof model !== \"object\" ||\n model === null ||\n !(\"client\" in model) ||\n !(\"_getClientOptions\" in model) ||\n typeof model._getClientOptions !== \"function\"\n ) {\n return false;\n }\n\n /**\n * client may not yet be initialized, so we need to check if the model has a _getClientOptions method.\n */\n model._getClientOptions();\n return (\n typeof model.client === \"object\" &&\n model.client !== null &&\n \"moderations\" in model.client &&\n typeof model.client.moderations === \"object\" &&\n model.client.moderations !== null &&\n \"create\" in model.client.moderations &&\n typeof model.client.moderations.create === \"function\"\n );\n}\n\n/**\n * Stage where a violation occurred.\n */\nexport type ViolationStage = \"input\" | \"output\" | \"tool\";\n\n/**\n * Default template for violation messages.\n */\nconst DEFAULT_VIOLATION_TEMPLATE =\n \"I'm sorry, but I can't comply with that request. It was flagged for {categories}.\";\n\n/**\n * Result of moderation.\n * @see https://platform.openai.com/docs/api-reference/moderations/object\n */\ninterface ModerationResult {\n flagged: boolean;\n categories: Record<string, boolean>;\n category_scores: Record<string, number>;\n category_applied_input_types: Record<string, string[]>;\n}\n\n/**\n * Moderation response.\n * @see https://platform.openai.com/docs/api-reference/moderations/create\n */\ninterface ModerationResponse {\n id: string;\n model: string;\n results: ModerationResult[];\n}\n\ntype ModerationModel =\n | \"omni-moderation-latest\"\n | \"omni-moderation-2024-09-26\"\n | \"text-moderation-latest\"\n | \"text-moderation-stable\";\n\n/**\n * Error raised when OpenAI flags content and `exitBehavior` is set to `\"error\"`.\n */\nexport class OpenAIModerationError extends Error {\n content: string;\n stage: ViolationStage;\n result: ModerationResult;\n originalMessage: string;\n\n constructor({\n content,\n stage,\n result,\n message,\n }: {\n content: string;\n stage: ViolationStage;\n result: ModerationResult;\n message: string;\n }) {\n super(message);\n this.name = \"OpenAIModerationError\";\n this.content = content;\n this.stage = stage;\n this.result = result;\n this.originalMessage = message;\n }\n}\n\n/**\n * Options for configuring the OpenAI Moderation middleware.\n */\nexport interface OpenAIModerationMiddlewareOptions {\n /**\n * OpenAI model to use for moderation. Can be either a model name or a BaseChatModel instance.\n * @example\n * ```ts\n * const model = new ChatOpenAI({ model: \"gpt-4o-mini\" });\n * const middleware = openAIModerationMiddleware({ model });\n * const agent = createAgent({\n * model,\n * middleware: [middleware],\n * });\n * ```\n * @example\n * ```ts\n * const middleware = openAIModerationMiddleware({ model: \"gpt-4o-mini\" });\n * const agent = createAgent({\n * model: \"gpt-5\",\n * middleware: [middleware],\n * });\n * ```\n */\n model: string | BaseChatModel;\n\n /**\n * Moderation model to use.\n * @default \"omni-moderation-latest\"\n */\n moderationModel?: ModerationModel;\n\n /**\n * Whether to check user input messages.\n * @default true\n */\n checkInput?: boolean;\n\n /**\n * Whether to check model output messages.\n * @default true\n */\n checkOutput?: boolean;\n\n /**\n * Whether to check tool result messages.\n * @default false\n */\n checkToolResults?: boolean;\n\n /**\n * How to handle violations.\n * - `\"error\"`: Throw an error when content is flagged\n * - `\"end\"`: End the agent execution and return a violation message\n * - `\"replace\"`: Replace the flagged content with a violation message\n * @default \"end\"\n */\n exitBehavior?: \"error\" | \"end\" | \"replace\";\n\n /**\n * Custom template for violation messages.\n * Available placeholders: `{categories}`, `{category_scores}`, `{original_content}`\n */\n violationMessage?: string;\n}\n\n/**\n * Middleware that moderates agent traffic using OpenAI's moderation endpoint.\n *\n * This middleware checks messages for content policy violations at different stages:\n * - Input: User messages before they reach the model\n * - Output: AI model responses\n * - Tool results: Results returned from tool executions\n *\n * @param options - Configuration options for the middleware\n * @param options.model - OpenAI model to use for moderation. Can be either a model name or a BaseChatModel instance.\n * @param options.moderationModel - Moderation model to use.\n * @param options.checkInput - Whether to check user input messages.\n * @param options.checkOutput - Whether to check model output messages.\n * @param options.checkToolResults - Whether to check tool result messages.\n * @param options.exitBehavior - How to handle violations.\n * @param options.violationMessage - Custom template for violation messages.\n * @returns Middleware function that can be used to moderate agent traffic.\n *\n * @example Using model instance\n * ```ts\n * import { createAgent, openAIModerationMiddleware } from \"langchain\";\n *\n * const middleware = openAIModerationMiddleware({\n * checkInput: true,\n * checkOutput: true,\n * exitBehavior: \"end\"\n * });\n *\n * const agent = createAgent({\n * model: \"openai:gpt-4o\",\n * tools: [...],\n * middleware: [middleware],\n * });\n * ```\n *\n * @example Using model name\n * ```ts\n * import { createAgent, openAIModerationMiddleware } from \"langchain\";\n *\n * const middleware = openAIModerationMiddleware({\n * model: \"gpt-4o-mini\",\n * checkInput: true,\n * checkOutput: true,\n * exitBehavior: \"end\"\n * });\n *\n * const agent = createAgent({\n * model: \"openai:gpt-4o\",\n * tools: [...],\n * middleware: [middleware],\n * });\n * ```\n *\n * @example Custom violation message\n * ```ts\n * const middleware = openAIModerationMiddleware({\n * violationMessage: \"Content flagged: {categories}. Scores: {category_scores}\"\n * });\n * ```\n */\nexport function openAIModerationMiddleware(\n options: OpenAIModerationMiddlewareOptions\n): AgentMiddleware {\n const {\n model,\n moderationModel = \"omni-moderation-latest\",\n checkInput = true,\n checkOutput = true,\n checkToolResults = false,\n exitBehavior = \"end\",\n violationMessage,\n } = options;\n\n let openaiModel: OpenAIModel | undefined;\n const initModerationModel = async (): Promise<OpenAIModel> => {\n if (openaiModel) {\n return openaiModel;\n }\n\n const resolvedModel =\n typeof model === \"string\" ? await initChatModel(model) : model;\n\n /**\n * Check if the model is an OpenAI model.\n */\n if (!resolvedModel.getName().includes(\"ChatOpenAI\")) {\n throw new Error(\n `Model must be an OpenAI model to use moderation middleware. Got: ${resolvedModel.getName()}`\n );\n }\n\n /**\n * check if OpenAI model package supports moderation.\n */\n if (!isOpenAIModel(resolvedModel)) {\n throw new Error(\n \"Model must support moderation to use moderation middleware.\"\n );\n }\n\n openaiModel = resolvedModel as unknown as OpenAIModel;\n return openaiModel;\n };\n\n /**\n * Extract text content from a message.\n */\n const extractText = (message: BaseMessage): string | null => {\n if (message.content == null) {\n return null;\n }\n const text = message.text;\n return text || null;\n };\n\n /**\n * Find the last index of a message type in the messages array.\n */\n const findLastIndex = (\n messages: BaseMessage[],\n messageType: typeof AIMessage | typeof HumanMessage | typeof ToolMessage\n ): number | null => {\n for (let idx = messages.length - 1; idx >= 0; idx--) {\n if (messageType.isInstance(messages[idx])) {\n return idx;\n }\n }\n return null;\n };\n\n /**\n * Format violation message from moderation result.\n */\n const formatViolationMessage = (\n content: string,\n result: ModerationResult\n ): string => {\n // Convert categories to array of flagged category names\n const categories: string[] = [];\n const categoriesObj = result.categories as unknown as Record<\n string,\n boolean\n >;\n for (const [name, flagged] of Object.entries(categoriesObj)) {\n if (flagged) {\n categories.push(name.replace(/_/g, \" \"));\n }\n }\n\n const categoryLabel =\n categories.length > 0\n ? categories.join(\", \")\n : \"OpenAI's safety policies\";\n\n const template = violationMessage || DEFAULT_VIOLATION_TEMPLATE;\n const scoresJson = JSON.stringify(\n result.category_scores as unknown as Record<string, number>,\n null,\n 2\n );\n\n try {\n return template\n .replace(\"{categories}\", categoryLabel)\n .replace(\"{category_scores}\", scoresJson)\n .replace(\"{original_content}\", content);\n } catch {\n return template;\n }\n };\n\n function moderateContent(\n input: string | string[],\n params?: { model?: ModerationModel; options?: unknown }\n ): Promise<ModerationResponse> {\n const clientOptions = openaiModel?._getClientOptions?.();\n const moderationModel = params?.model ?? \"omni-moderation-latest\";\n const moderationRequest = {\n input,\n model: moderationModel,\n };\n return openaiModel!.client.moderations.create(\n moderationRequest,\n clientOptions\n );\n }\n\n /**\n * Apply violation handling based on exit behavior.\n */\n const applyViolation = (\n messages: BaseMessage[],\n index: number | null,\n stage: ViolationStage,\n content: string,\n result: ModerationResult\n ): MiddlewareResult<Partial<AgentBuiltInState>> | undefined => {\n const violationText = formatViolationMessage(content, result);\n\n if (exitBehavior === \"error\") {\n throw new OpenAIModerationError({\n content,\n stage,\n result,\n message: violationText,\n });\n }\n\n if (exitBehavior === \"end\") {\n return {\n jumpTo: \"end\",\n messages: [new AIMessage({ content: violationText })],\n };\n }\n\n if (index == null) {\n return undefined;\n }\n\n /**\n * Replace the original message with a new message that contains the violation text.\n */\n const newMessages = [...messages];\n const original = newMessages[index];\n const MessageConstructor = Object.getPrototypeOf(original).constructor;\n newMessages[index] = new MessageConstructor({\n ...original,\n content: violationText,\n });\n\n return { messages: newMessages };\n };\n\n /**\n * Moderate user input messages.\n */\n const moderateUserMessage = async (\n messages: BaseMessage[]\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | null> => {\n const idx = findLastIndex(messages, HumanMessage);\n if (idx == null) {\n return null;\n }\n\n const message = messages[idx];\n const text = extractText(message);\n if (!text) {\n return null;\n }\n\n await initModerationModel();\n const response = await moderateContent(text, {\n model: moderationModel,\n });\n\n const flaggedResult = response.results.find((result) => result.flagged);\n if (!flaggedResult) {\n return null;\n }\n\n return applyViolation(messages, idx, \"input\", text, flaggedResult);\n };\n\n /**\n * Moderate tool result messages.\n */\n const moderateToolMessages = async (\n messages: BaseMessage[]\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | null> => {\n const lastAiIdx = findLastIndex(messages, AIMessage);\n if (lastAiIdx == null) {\n return null;\n }\n\n const working = [...messages];\n let modified = false;\n\n for (let idx = lastAiIdx + 1; idx < working.length; idx++) {\n const msg = working[idx];\n if (!ToolMessage.isInstance(msg)) {\n continue;\n }\n\n const text = extractText(msg);\n if (!text) {\n continue;\n }\n\n await initModerationModel();\n const response = await moderateContent(text, {\n model: moderationModel,\n });\n const flaggedResult = response.results.find((result) => result.flagged);\n if (!flaggedResult) {\n continue;\n }\n\n const action = applyViolation(working, idx, \"tool\", text, flaggedResult);\n if (action) {\n if (\"jumpTo\" in action) {\n return action;\n }\n if (\"messages\" in action) {\n working.splice(\n 0,\n working.length,\n ...(action.messages as BaseMessage[])\n );\n modified = true;\n }\n }\n }\n\n if (modified) {\n return { messages: working };\n }\n\n return null;\n };\n\n /**\n * Moderate model output messages.\n */\n const moderateOutput = async (\n messages: BaseMessage[]\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | null> => {\n const lastAiIdx = findLastIndex(messages, AIMessage);\n if (lastAiIdx == null) {\n return null;\n }\n\n const aiMessage = messages[lastAiIdx];\n const text = extractText(aiMessage);\n if (!text) {\n return null;\n }\n\n await initModerationModel();\n const response = await moderateContent(text, {\n model: moderationModel,\n });\n const flaggedResult = response.results.find((result) => result.flagged);\n if (!flaggedResult) {\n return null;\n }\n\n return applyViolation(messages, lastAiIdx, \"output\", text, flaggedResult);\n };\n\n /**\n * Moderate inputs (user messages and tool results) before model call.\n */\n const moderateInputs = async (\n messages: BaseMessage[]\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | null> => {\n const working = [...messages];\n let modified = false;\n\n if (checkToolResults) {\n const action = await moderateToolMessages(working);\n if (action) {\n if (\"jumpTo\" in action) {\n return action;\n }\n if (\"messages\" in action) {\n working.splice(\n 0,\n working.length,\n ...(action.messages as BaseMessage[])\n );\n modified = true;\n }\n }\n }\n\n if (checkInput) {\n const action = await moderateUserMessage(working);\n if (action) {\n if (\"jumpTo\" in action) {\n return action;\n }\n if (\"messages\" in action) {\n working.splice(\n 0,\n working.length,\n ...(action.messages as BaseMessage[])\n );\n modified = true;\n }\n }\n }\n\n if (modified) {\n return { messages: working };\n }\n\n return null;\n };\n\n return createMiddleware({\n name: \"OpenAIModerationMiddleware\",\n beforeModel: {\n hook: async (\n state\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | undefined> => {\n if (!checkInput && !checkToolResults) {\n return undefined;\n }\n\n const messages = state.messages || [];\n if (messages.length === 0) {\n return undefined;\n }\n\n return (await moderateInputs(messages)) ?? undefined;\n },\n canJumpTo: [\"end\"],\n },\n afterModel: {\n hook: async (\n state\n ): Promise<MiddlewareResult<Partial<AgentBuiltInState>> | undefined> => {\n if (!checkOutput) {\n return undefined;\n }\n\n const messages = state.messages || [];\n if (messages.length === 0) {\n return undefined;\n }\n\n return (await moderateOutput(messages)) ?? undefined;\n },\n canJumpTo: [\"end\"],\n },\n });\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providerToolSearch.cjs","names":["createMiddleware","isLangChainTool"],"sources":["../../../src/agents/middleware/providerToolSearch.ts"],"sourcesContent":["import type {\n StructuredToolInterface,\n StructuredToolParams,\n ClientTool,\n ServerTool,\n} from \"@langchain/core/tools\";\nimport { isLangChainTool } from \"@langchain/core/utils/function_calling\";\nimport { createMiddleware } from \"../middleware.js\";\nimport type { LanguageModelLike } from \"@langchain/core/language_models/base\";\nimport type {\n ChatModelProvider,\n ConfigurableModel,\n} from \"../../chat_models/universal.js\";\n\ntype ToolName = string;\nexport type ToolIdentifier = ToolName | StructuredToolInterface;\n\nexport type ProviderToolSearchMiddlewareConfig = {\n /**\n * Which tools are deferred; withheld from the model until its tool search surfaces them.\n *\n * Tools already constructed with `extras.defer_loading === true` are deferred\n * regardless of this option; if `searchableTools` is omitted, only those pre-marked\n * tools are deferred.\n */\n searchableTools?: ToolIdentifier[];\n};\n\nconst SERVER_SEARCH_PROVIDERS = [\n \"anthropic\",\n \"openai\",\n] as const satisfies readonly ChatModelProvider[];\n\ntype ServerSearchCapableProvider = (typeof SERVER_SEARCH_PROVIDERS)[number];\ntype DetectedProvider = ServerSearchCapableProvider | \"other\";\n\ntype ServerToolSearchTool = { type: string; name?: string };\n\nconst SERVER_TOOL_SEARCH_TOOLS = {\n anthropic: {\n type: \"tool_search_tool_bm25_20251119\",\n name: \"tool_search_tool_bm25\",\n },\n openai: { type: \"tool_search\" },\n} as const satisfies Record<ServerSearchCapableProvider, ServerToolSearchTool>;\n\n/**\n * Provider-side tool search middleware.\n *\n * Leverages server-side tool search: the full client tool catalog is forwarded\n * to the provider, with deferred tools marked `defer_loading` so the provider\n * discloses them on demand via its own search. A tool is deferred when it is\n * named in `searchableTools` or built with `extras.defer_loading: true`.\n *\n * Requires a model with server-side tool search support: OpenAI gpt-5.4+ or Anthropic\n * Claude Sonnet 4+/Opus 4+/Haiku 4.5+. Non-Anthropic/OpenAI providers throw; an\n * in-family model that is too old surfaces the provider's own API error rather\n * than being gated here.\n *\n * @example\n * ```ts\n * import { createAgent, providerToolSearchMiddleware } from \"langchain\";\n * import { ChatAnthropic } from \"@langchain/anthropic\";\n *\n * const agent = createAgent({\n * model: new ChatAnthropic({ model: \"claude-sonnet-4-5\" }),\n * tools: [getWeather, ...nicheTools],\n * middleware: [\n * // Defer the niche tools behind the provider's tool search; the model\n * // discovers them on demand instead of receiving every schema up front.\n * providerToolSearchMiddleware({ searchableTools: nicheTools }),\n * ],\n * });\n * ```\n *\n * @example\n * ```ts\n * import { tool } from \"@langchain/core/tools\";\n * import { createAgent, providerToolSearchMiddleware } from \"langchain\";\n * import { ChatAnthropic } from \"@langchain/anthropic\";\n *\n * // A tool marked `defer_loading` at construction is deferred on its own —\n * // no need to list it in `searchableTools`; the middleware honors the flag.\n * const sendEmail = tool(sendEmailFn, {\n * name: \"send_email\",\n * description: \"Send an email\",\n * schema: sendEmailSchema,\n * extras: { defer_loading: true },\n * });\n *\n * const agent = createAgent({\n * model: new ChatAnthropic({ model: \"claude-sonnet-4-5\" }),\n * tools: [getWeather, sendEmail],\n * middleware: [providerToolSearchMiddleware()],\n * });\n * ```\n *\n * @param config - Configuration options for the middleware\n * @param config.searchableTools - Tools to defer behind tool search\n * @returns A middleware instance that can be used with `createAgent`\n */\nexport function providerToolSearchMiddleware(\n config: ProviderToolSearchMiddlewareConfig = {}\n) {\n const deferNames = toToolNames(config.searchableTools);\n\n return createMiddleware({\n name: \"ProviderToolSearch\",\n wrapModelCall: (request, handler) => {\n const tools = request.tools ?? [];\n\n // Fail fast if we try to defer a tool that is not bound to the model\n if (deferNames.size > 0) {\n const available = tools.filter(isLangChainTool).map((t) => t.name);\n const unknown = [...deferNames].filter(\n (name) => !available.includes(name)\n );\n if (unknown.length > 0) {\n throw new Error(\n `providerToolSearchMiddleware: searchableTools references tool(s) not bound to the model: ${unknown.join(\", \")}`\n );\n }\n }\n\n const provider = getModelProvider(request.model);\n if (!supportsProviderToolSearch(provider)) {\n throw new Error(\n `providerToolSearchMiddleware requires a provider with server-side tool search, but got ${provider}`\n );\n }\n\n // Nothing to defer -> pass thru\n if (!hasDeferredTools(tools, deferNames)) return handler(request);\n\n // For each deferred tool, emit a minimal binding spec carrying `defer_loading`.\n const boundTools = tools.map((tool) =>\n deferToolIfNeeded(tool, deferNames)\n );\n\n const nativeSearchTool = SERVER_TOOL_SEARCH_TOOLS[provider];\n return handler({ ...request, tools: [...boundTools, nativeSearchTool] });\n },\n });\n}\n\nfunction isDeferred(\n tool: unknown,\n deferNames: Set<string>\n): tool is StructuredToolParams {\n return (\n isLangChainTool(tool) &&\n (tool.extras?.defer_loading === true || deferNames.has(tool.name))\n );\n}\n\nfunction hasDeferredTools(\n tools: readonly (ClientTool | ServerTool)[],\n deferNames: Set<string>\n): boolean {\n return tools.some((tool) => isDeferred(tool, deferNames));\n}\n\n/**\n * If a tool should be deferred, return a minimal binding spec carrying `defer_loading`.\n * Otherwise return it as-is\n */\nfunction deferToolIfNeeded(\n tool: ClientTool | ServerTool,\n deferNames: Set<string>\n): ClientTool | ServerTool {\n if (!isDeferred(tool, deferNames)) return tool;\n return {\n name: tool.name,\n description: tool.description,\n schema: tool.schema,\n extras: { ...tool.extras, defer_loading: true },\n };\n}\n\n/** Flatten a list of tool names/instances into a set of tool names. */\nfunction toToolNames(tools: ToolIdentifier[] = []): Set<string> {\n return new Set(tools.map((t) => (typeof t === \"string\" ? t : t.name)));\n}\n\nfunction getModelProvider(model: LanguageModelLike): DetectedProvider {\n const name = model.getName();\n const configured =\n name === \"ConfigurableModel\"\n ? (model as ConfigurableModel)._defaultConfig?.modelProvider\n : undefined;\n if (name === \"ChatAnthropic\" || configured === \"anthropic\")\n return \"anthropic\";\n if (name === \"ChatOpenAI\" || configured === \"openai\") return \"openai\";\n return \"other\";\n}\n\nfunction supportsProviderToolSearch(\n provider: DetectedProvider\n): provider is ServerSearchCapableProvider {\n return (SERVER_SEARCH_PROVIDERS as readonly string[]).includes(provider);\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"providerToolSearch.cjs","names":["createMiddleware","isLangChainTool"],"sources":["../../../src/agents/middleware/providerToolSearch.ts"],"sourcesContent":["import type {\n StructuredToolInterface,\n StructuredToolParams,\n ClientTool,\n ServerTool,\n} from \"@langchain/core/tools\";\nimport { isLangChainTool } from \"@langchain/core/utils/function_calling\";\nimport { createMiddleware } from \"../middleware.js\";\nimport type { LanguageModelLike } from \"@langchain/core/language_models/base\";\nimport type {\n ChatModelProvider,\n ConfigurableModel,\n} from \"../../chat_models/universal.js\";\n\ntype ToolName = string;\nexport type ToolIdentifier = ToolName | StructuredToolInterface;\n\nexport type ProviderToolSearchMiddlewareConfig = {\n /**\n * Which tools are deferred; withheld from the model until its tool search surfaces them.\n *\n * Tools already constructed with `extras.defer_loading === true` are deferred\n * regardless of this option; if `searchableTools` is omitted, only those pre-marked\n * tools are deferred.\n */\n searchableTools?: ToolIdentifier[];\n};\n\nconst SERVER_SEARCH_PROVIDERS = [\n \"anthropic\",\n \"openai\",\n] as const satisfies readonly ChatModelProvider[];\n\ntype ServerSearchCapableProvider = (typeof SERVER_SEARCH_PROVIDERS)[number];\ntype DetectedProvider = ServerSearchCapableProvider | \"other\";\n\ntype ServerToolSearchTool = { type: string; name?: string };\n\nconst SERVER_TOOL_SEARCH_TOOLS = {\n anthropic: {\n type: \"tool_search_tool_bm25_20251119\",\n name: \"tool_search_tool_bm25\",\n },\n openai: { type: \"tool_search\" },\n} as const satisfies Record<ServerSearchCapableProvider, ServerToolSearchTool>;\n\n/**\n * Provider-side tool search middleware.\n *\n * Leverages server-side tool search: the full client tool catalog is forwarded\n * to the provider, with deferred tools marked `defer_loading` so the provider\n * discloses them on demand via its own search. A tool is deferred when it is\n * named in `searchableTools` or built with `extras.defer_loading: true`.\n *\n * Requires a model with server-side tool search support: OpenAI gpt-5.4+ or Anthropic\n * Claude Sonnet 4+/Opus 4+/Haiku 4.5+. Non-Anthropic/OpenAI providers throw; an\n * in-family model that is too old surfaces the provider's own API error rather\n * than being gated here.\n *\n * @example\n * ```ts\n * import { createAgent, providerToolSearchMiddleware } from \"langchain\";\n * import { ChatAnthropic } from \"@langchain/anthropic\";\n *\n * const agent = createAgent({\n * model: new ChatAnthropic({ model: \"claude-sonnet-4-5\" }),\n * tools: [getWeather, ...nicheTools],\n * middleware: [\n * // Defer the niche tools behind the provider's tool search; the model\n * // discovers them on demand instead of receiving every schema up front.\n * providerToolSearchMiddleware({ searchableTools: nicheTools }),\n * ],\n * });\n * ```\n *\n * @example\n * ```ts\n * import { tool } from \"@langchain/core/tools\";\n * import { createAgent, providerToolSearchMiddleware } from \"langchain\";\n * import { ChatAnthropic } from \"@langchain/anthropic\";\n *\n * // A tool marked `defer_loading` at construction is deferred on its own —\n * // no need to list it in `searchableTools`; the middleware honors the flag.\n * const sendEmail = tool(sendEmailFn, {\n * name: \"send_email\",\n * description: \"Send an email\",\n * schema: sendEmailSchema,\n * extras: { defer_loading: true },\n * });\n *\n * const agent = createAgent({\n * model: new ChatAnthropic({ model: \"claude-sonnet-4-5\" }),\n * tools: [getWeather, sendEmail],\n * middleware: [providerToolSearchMiddleware()],\n * });\n * ```\n *\n * @param config - Configuration options for the middleware\n * @param config.searchableTools - Tools to defer behind tool search\n * @returns A middleware instance that can be used with `createAgent`\n */\nexport function providerToolSearchMiddleware(\n config: ProviderToolSearchMiddlewareConfig = {}\n) {\n const deferNames = toToolNames(config.searchableTools);\n\n return createMiddleware({\n name: \"ProviderToolSearch\",\n wrapModelCall: (request, handler) => {\n const tools = request.tools ?? [];\n\n // Fail fast if we try to defer a tool that is not bound to the model\n if (deferNames.size > 0) {\n const available = tools.filter(isLangChainTool).map((t) => t.name);\n const unknown = [...deferNames].filter(\n (name) => !available.includes(name)\n );\n if (unknown.length > 0) {\n throw new Error(\n `providerToolSearchMiddleware: searchableTools references tool(s) not bound to the model: ${unknown.join(\", \")}`\n );\n }\n }\n\n const provider = getModelProvider(request.model);\n if (!supportsProviderToolSearch(provider)) {\n throw new Error(\n `providerToolSearchMiddleware requires a provider with server-side tool search, but got ${provider}`\n );\n }\n\n // Nothing to defer -> pass thru\n if (!hasDeferredTools(tools, deferNames)) return handler(request);\n\n // For each deferred tool, emit a minimal binding spec carrying `defer_loading`.\n const boundTools = tools.map((tool) =>\n deferToolIfNeeded(tool, deferNames)\n );\n\n const nativeSearchTool = SERVER_TOOL_SEARCH_TOOLS[provider];\n return handler({ ...request, tools: [...boundTools, nativeSearchTool] });\n },\n });\n}\n\nfunction isDeferred(\n tool: unknown,\n deferNames: Set<string>\n): tool is StructuredToolParams {\n return (\n isLangChainTool(tool) &&\n (tool.extras?.defer_loading === true || deferNames.has(tool.name))\n );\n}\n\nfunction hasDeferredTools(\n tools: readonly (ClientTool | ServerTool)[],\n deferNames: Set<string>\n): boolean {\n return tools.some((tool) => isDeferred(tool, deferNames));\n}\n\n/**\n * If a tool should be deferred, return a minimal binding spec carrying `defer_loading`.\n * Otherwise return it as-is\n */\nfunction deferToolIfNeeded(\n tool: ClientTool | ServerTool,\n deferNames: Set<string>\n): ClientTool | ServerTool {\n if (!isDeferred(tool, deferNames)) return tool;\n return {\n name: tool.name,\n description: tool.description,\n schema: tool.schema,\n extras: { ...tool.extras, defer_loading: true },\n };\n}\n\n/** Flatten a list of tool names/instances into a set of tool names. */\nfunction toToolNames(tools: ToolIdentifier[] = []): Set<string> {\n return new Set(tools.map((t) => (typeof t === \"string\" ? t : t.name)));\n}\n\nfunction getModelProvider(model: LanguageModelLike): DetectedProvider {\n const name = model.getName();\n const configured =\n name === \"ConfigurableModel\"\n ? (model as ConfigurableModel)._defaultConfig?.modelProvider\n : undefined;\n if (name === \"ChatAnthropic\" || configured === \"anthropic\")\n return \"anthropic\";\n if (name === \"ChatOpenAI\" || configured === \"openai\") return \"openai\";\n return \"other\";\n}\n\nfunction supportsProviderToolSearch(\n provider: DetectedProvider\n): provider is ServerSearchCapableProvider {\n return (SERVER_SEARCH_PROVIDERS as readonly string[]).includes(provider);\n}\n"],"mappings":";;;AA4BA,MAAM,0BAA0B,CAC9B,aACA,QACF;AAOA,MAAM,2BAA2B;CAC/B,WAAW;EACT,MAAM;EACN,MAAM;CACR;CACA,QAAQ,EAAE,MAAM,cAAc;AAChC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDA,SAAgB,6BACd,SAA6C,CAAC,GAC9C;CACA,MAAM,aAAa,YAAY,OAAO,eAAe;CAErD,OAAOA,mBAAAA,iBAAiB;EACtB,MAAM;EACN,gBAAgB,SAAS,YAAY;GACnC,MAAM,QAAQ,QAAQ,SAAS,CAAC;GAGhC,IAAI,WAAW,OAAO,GAAG;IACvB,MAAM,YAAY,MAAM,OAAOC,uCAAAA,eAAe,CAAC,CAAC,KAAK,MAAM,EAAE,IAAI;IACjE,MAAM,UAAU,CAAC,GAAG,UAAU,CAAC,CAAC,QAC7B,SAAS,CAAC,UAAU,SAAS,IAAI,CACpC;IACA,IAAI,QAAQ,SAAS,GACnB,MAAM,IAAI,MACR,4FAA4F,QAAQ,KAAK,IAAI,GAC/G;GAEJ;GAEA,MAAM,WAAW,iBAAiB,QAAQ,KAAK;GAC/C,IAAI,CAAC,2BAA2B,QAAQ,GACtC,MAAM,IAAI,MACR,0FAA0F,UAC5F;GAIF,IAAI,CAAC,iBAAiB,OAAO,UAAU,GAAG,OAAO,QAAQ,OAAO;GAGhE,MAAM,aAAa,MAAM,KAAK,SAC5B,kBAAkB,MAAM,UAAU,CACpC;GAEA,MAAM,mBAAmB,yBAAyB;GAClD,OAAO,QAAQ;IAAE,GAAG;IAAS,OAAO,CAAC,GAAG,YAAY,gBAAgB;GAAE,CAAC;EACzE;CACF,CAAC;AACH;AAEA,SAAS,WACP,MACA,YAC8B;CAC9B,QAAA,GAAA,uCAAA,gBAAA,CACkB,IAAI,MACnB,KAAK,QAAQ,kBAAkB,QAAQ,WAAW,IAAI,KAAK,IAAI;AAEpE;AAEA,SAAS,iBACP,OACA,YACS;CACT,OAAO,MAAM,MAAM,SAAS,WAAW,MAAM,UAAU,CAAC;AAC1D;;;;;AAMA,SAAS,kBACP,MACA,YACyB;CACzB,IAAI,CAAC,WAAW,MAAM,UAAU,GAAG,OAAO;CAC1C,OAAO;EACL,MAAM,KAAK;EACX,aAAa,KAAK;EAClB,QAAQ,KAAK;EACb,QAAQ;GAAE,GAAG,KAAK;GAAQ,eAAe;EAAK;CAChD;AACF;;AAGA,SAAS,YAAY,QAA0B,CAAC,GAAgB;CAC9D,OAAO,IAAI,IAAI,MAAM,KAAK,MAAO,OAAO,MAAM,WAAW,IAAI,EAAE,IAAK,CAAC;AACvE;AAEA,SAAS,iBAAiB,OAA4C;CACpE,MAAM,OAAO,MAAM,QAAQ;CAC3B,MAAM,aACJ,SAAS,sBACJ,MAA4B,gBAAgB,gBAC7C,KAAA;CACN,IAAI,SAAS,mBAAmB,eAAe,aAC7C,OAAO;CACT,IAAI,SAAS,gBAAgB,eAAe,UAAU,OAAO;CAC7D,OAAO;AACT;AAEA,SAAS,2BACP,UACyC;CACzC,OAAQ,wBAA8C,SAAS,QAAQ;AACzE"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { AgentMiddleware } from "./types.cjs";
|
|
2
2
|
import { ClientTool, ServerTool, StructuredToolInterface } from "@langchain/core/tools";
|
|
3
|
-
|
|
4
3
|
//#region src/agents/middleware/providerToolSearch.d.ts
|
|
5
4
|
type ToolName = string;
|
|
6
5
|
type ToolIdentifier = ToolName | StructuredToolInterface;
|
|
@@ -71,5 +70,5 @@ type ProviderToolSearchMiddlewareConfig = {
|
|
|
71
70
|
*/
|
|
72
71
|
declare function providerToolSearchMiddleware(config?: ProviderToolSearchMiddlewareConfig): AgentMiddleware<undefined, undefined, unknown, readonly (ServerTool | ClientTool)[], readonly []>;
|
|
73
72
|
//#endregion
|
|
74
|
-
export { ProviderToolSearchMiddlewareConfig, providerToolSearchMiddleware };
|
|
73
|
+
export { ProviderToolSearchMiddlewareConfig, ToolIdentifier, providerToolSearchMiddleware };
|
|
75
74
|
//# sourceMappingURL=providerToolSearch.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providerToolSearch.d.cts","names":[],"sources":["../../../src/agents/middleware/providerToolSearch.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"providerToolSearch.d.cts","names":[],"sources":["../../../src/agents/middleware/providerToolSearch.ts"],"mappings":";;;KAcK;KACO,iBAAiB,WAAW;KAE5B;;;;;;;;EAQV,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4EJ,6BACd,SAAQ,qCAAuC,yDAAA,aAAA"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { AgentMiddleware } from "./types.js";
|
|
2
2
|
import { ClientTool, ServerTool, StructuredToolInterface } from "@langchain/core/tools";
|
|
3
|
-
|
|
4
3
|
//#region src/agents/middleware/providerToolSearch.d.ts
|
|
5
4
|
type ToolName = string;
|
|
6
5
|
type ToolIdentifier = ToolName | StructuredToolInterface;
|
|
@@ -71,5 +70,5 @@ type ProviderToolSearchMiddlewareConfig = {
|
|
|
71
70
|
*/
|
|
72
71
|
declare function providerToolSearchMiddleware(config?: ProviderToolSearchMiddlewareConfig): AgentMiddleware<undefined, undefined, unknown, readonly (ServerTool | ClientTool)[], readonly []>;
|
|
73
72
|
//#endregion
|
|
74
|
-
export { ProviderToolSearchMiddlewareConfig, providerToolSearchMiddleware };
|
|
73
|
+
export { ProviderToolSearchMiddlewareConfig, ToolIdentifier, providerToolSearchMiddleware };
|
|
75
74
|
//# sourceMappingURL=providerToolSearch.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providerToolSearch.d.ts","names":[],"sources":["../../../src/agents/middleware/providerToolSearch.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"providerToolSearch.d.ts","names":[],"sources":["../../../src/agents/middleware/providerToolSearch.ts"],"mappings":";;;KAcK;KACO,iBAAiB,WAAW;KAE5B;;;;;;;;EAQV,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4EJ,6BACd,SAAQ,qCAAuC,yDAAA,aAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providerToolSearch.js","names":[],"sources":["../../../src/agents/middleware/providerToolSearch.ts"],"sourcesContent":["import type {\n StructuredToolInterface,\n StructuredToolParams,\n ClientTool,\n ServerTool,\n} from \"@langchain/core/tools\";\nimport { isLangChainTool } from \"@langchain/core/utils/function_calling\";\nimport { createMiddleware } from \"../middleware.js\";\nimport type { LanguageModelLike } from \"@langchain/core/language_models/base\";\nimport type {\n ChatModelProvider,\n ConfigurableModel,\n} from \"../../chat_models/universal.js\";\n\ntype ToolName = string;\nexport type ToolIdentifier = ToolName | StructuredToolInterface;\n\nexport type ProviderToolSearchMiddlewareConfig = {\n /**\n * Which tools are deferred; withheld from the model until its tool search surfaces them.\n *\n * Tools already constructed with `extras.defer_loading === true` are deferred\n * regardless of this option; if `searchableTools` is omitted, only those pre-marked\n * tools are deferred.\n */\n searchableTools?: ToolIdentifier[];\n};\n\nconst SERVER_SEARCH_PROVIDERS = [\n \"anthropic\",\n \"openai\",\n] as const satisfies readonly ChatModelProvider[];\n\ntype ServerSearchCapableProvider = (typeof SERVER_SEARCH_PROVIDERS)[number];\ntype DetectedProvider = ServerSearchCapableProvider | \"other\";\n\ntype ServerToolSearchTool = { type: string; name?: string };\n\nconst SERVER_TOOL_SEARCH_TOOLS = {\n anthropic: {\n type: \"tool_search_tool_bm25_20251119\",\n name: \"tool_search_tool_bm25\",\n },\n openai: { type: \"tool_search\" },\n} as const satisfies Record<ServerSearchCapableProvider, ServerToolSearchTool>;\n\n/**\n * Provider-side tool search middleware.\n *\n * Leverages server-side tool search: the full client tool catalog is forwarded\n * to the provider, with deferred tools marked `defer_loading` so the provider\n * discloses them on demand via its own search. A tool is deferred when it is\n * named in `searchableTools` or built with `extras.defer_loading: true`.\n *\n * Requires a model with server-side tool search support: OpenAI gpt-5.4+ or Anthropic\n * Claude Sonnet 4+/Opus 4+/Haiku 4.5+. Non-Anthropic/OpenAI providers throw; an\n * in-family model that is too old surfaces the provider's own API error rather\n * than being gated here.\n *\n * @example\n * ```ts\n * import { createAgent, providerToolSearchMiddleware } from \"langchain\";\n * import { ChatAnthropic } from \"@langchain/anthropic\";\n *\n * const agent = createAgent({\n * model: new ChatAnthropic({ model: \"claude-sonnet-4-5\" }),\n * tools: [getWeather, ...nicheTools],\n * middleware: [\n * // Defer the niche tools behind the provider's tool search; the model\n * // discovers them on demand instead of receiving every schema up front.\n * providerToolSearchMiddleware({ searchableTools: nicheTools }),\n * ],\n * });\n * ```\n *\n * @example\n * ```ts\n * import { tool } from \"@langchain/core/tools\";\n * import { createAgent, providerToolSearchMiddleware } from \"langchain\";\n * import { ChatAnthropic } from \"@langchain/anthropic\";\n *\n * // A tool marked `defer_loading` at construction is deferred on its own —\n * // no need to list it in `searchableTools`; the middleware honors the flag.\n * const sendEmail = tool(sendEmailFn, {\n * name: \"send_email\",\n * description: \"Send an email\",\n * schema: sendEmailSchema,\n * extras: { defer_loading: true },\n * });\n *\n * const agent = createAgent({\n * model: new ChatAnthropic({ model: \"claude-sonnet-4-5\" }),\n * tools: [getWeather, sendEmail],\n * middleware: [providerToolSearchMiddleware()],\n * });\n * ```\n *\n * @param config - Configuration options for the middleware\n * @param config.searchableTools - Tools to defer behind tool search\n * @returns A middleware instance that can be used with `createAgent`\n */\nexport function providerToolSearchMiddleware(\n config: ProviderToolSearchMiddlewareConfig = {}\n) {\n const deferNames = toToolNames(config.searchableTools);\n\n return createMiddleware({\n name: \"ProviderToolSearch\",\n wrapModelCall: (request, handler) => {\n const tools = request.tools ?? [];\n\n // Fail fast if we try to defer a tool that is not bound to the model\n if (deferNames.size > 0) {\n const available = tools.filter(isLangChainTool).map((t) => t.name);\n const unknown = [...deferNames].filter(\n (name) => !available.includes(name)\n );\n if (unknown.length > 0) {\n throw new Error(\n `providerToolSearchMiddleware: searchableTools references tool(s) not bound to the model: ${unknown.join(\", \")}`\n );\n }\n }\n\n const provider = getModelProvider(request.model);\n if (!supportsProviderToolSearch(provider)) {\n throw new Error(\n `providerToolSearchMiddleware requires a provider with server-side tool search, but got ${provider}`\n );\n }\n\n // Nothing to defer -> pass thru\n if (!hasDeferredTools(tools, deferNames)) return handler(request);\n\n // For each deferred tool, emit a minimal binding spec carrying `defer_loading`.\n const boundTools = tools.map((tool) =>\n deferToolIfNeeded(tool, deferNames)\n );\n\n const nativeSearchTool = SERVER_TOOL_SEARCH_TOOLS[provider];\n return handler({ ...request, tools: [...boundTools, nativeSearchTool] });\n },\n });\n}\n\nfunction isDeferred(\n tool: unknown,\n deferNames: Set<string>\n): tool is StructuredToolParams {\n return (\n isLangChainTool(tool) &&\n (tool.extras?.defer_loading === true || deferNames.has(tool.name))\n );\n}\n\nfunction hasDeferredTools(\n tools: readonly (ClientTool | ServerTool)[],\n deferNames: Set<string>\n): boolean {\n return tools.some((tool) => isDeferred(tool, deferNames));\n}\n\n/**\n * If a tool should be deferred, return a minimal binding spec carrying `defer_loading`.\n * Otherwise return it as-is\n */\nfunction deferToolIfNeeded(\n tool: ClientTool | ServerTool,\n deferNames: Set<string>\n): ClientTool | ServerTool {\n if (!isDeferred(tool, deferNames)) return tool;\n return {\n name: tool.name,\n description: tool.description,\n schema: tool.schema,\n extras: { ...tool.extras, defer_loading: true },\n };\n}\n\n/** Flatten a list of tool names/instances into a set of tool names. */\nfunction toToolNames(tools: ToolIdentifier[] = []): Set<string> {\n return new Set(tools.map((t) => (typeof t === \"string\" ? t : t.name)));\n}\n\nfunction getModelProvider(model: LanguageModelLike): DetectedProvider {\n const name = model.getName();\n const configured =\n name === \"ConfigurableModel\"\n ? (model as ConfigurableModel)._defaultConfig?.modelProvider\n : undefined;\n if (name === \"ChatAnthropic\" || configured === \"anthropic\")\n return \"anthropic\";\n if (name === \"ChatOpenAI\" || configured === \"openai\") return \"openai\";\n return \"other\";\n}\n\nfunction supportsProviderToolSearch(\n provider: DetectedProvider\n): provider is ServerSearchCapableProvider {\n return (SERVER_SEARCH_PROVIDERS as readonly string[]).includes(provider);\n}\n"],"mappings":";;;AA4BA,MAAM,0BAA0B,CAC9B,aACA,
|
|
1
|
+
{"version":3,"file":"providerToolSearch.js","names":[],"sources":["../../../src/agents/middleware/providerToolSearch.ts"],"sourcesContent":["import type {\n StructuredToolInterface,\n StructuredToolParams,\n ClientTool,\n ServerTool,\n} from \"@langchain/core/tools\";\nimport { isLangChainTool } from \"@langchain/core/utils/function_calling\";\nimport { createMiddleware } from \"../middleware.js\";\nimport type { LanguageModelLike } from \"@langchain/core/language_models/base\";\nimport type {\n ChatModelProvider,\n ConfigurableModel,\n} from \"../../chat_models/universal.js\";\n\ntype ToolName = string;\nexport type ToolIdentifier = ToolName | StructuredToolInterface;\n\nexport type ProviderToolSearchMiddlewareConfig = {\n /**\n * Which tools are deferred; withheld from the model until its tool search surfaces them.\n *\n * Tools already constructed with `extras.defer_loading === true` are deferred\n * regardless of this option; if `searchableTools` is omitted, only those pre-marked\n * tools are deferred.\n */\n searchableTools?: ToolIdentifier[];\n};\n\nconst SERVER_SEARCH_PROVIDERS = [\n \"anthropic\",\n \"openai\",\n] as const satisfies readonly ChatModelProvider[];\n\ntype ServerSearchCapableProvider = (typeof SERVER_SEARCH_PROVIDERS)[number];\ntype DetectedProvider = ServerSearchCapableProvider | \"other\";\n\ntype ServerToolSearchTool = { type: string; name?: string };\n\nconst SERVER_TOOL_SEARCH_TOOLS = {\n anthropic: {\n type: \"tool_search_tool_bm25_20251119\",\n name: \"tool_search_tool_bm25\",\n },\n openai: { type: \"tool_search\" },\n} as const satisfies Record<ServerSearchCapableProvider, ServerToolSearchTool>;\n\n/**\n * Provider-side tool search middleware.\n *\n * Leverages server-side tool search: the full client tool catalog is forwarded\n * to the provider, with deferred tools marked `defer_loading` so the provider\n * discloses them on demand via its own search. A tool is deferred when it is\n * named in `searchableTools` or built with `extras.defer_loading: true`.\n *\n * Requires a model with server-side tool search support: OpenAI gpt-5.4+ or Anthropic\n * Claude Sonnet 4+/Opus 4+/Haiku 4.5+. Non-Anthropic/OpenAI providers throw; an\n * in-family model that is too old surfaces the provider's own API error rather\n * than being gated here.\n *\n * @example\n * ```ts\n * import { createAgent, providerToolSearchMiddleware } from \"langchain\";\n * import { ChatAnthropic } from \"@langchain/anthropic\";\n *\n * const agent = createAgent({\n * model: new ChatAnthropic({ model: \"claude-sonnet-4-5\" }),\n * tools: [getWeather, ...nicheTools],\n * middleware: [\n * // Defer the niche tools behind the provider's tool search; the model\n * // discovers them on demand instead of receiving every schema up front.\n * providerToolSearchMiddleware({ searchableTools: nicheTools }),\n * ],\n * });\n * ```\n *\n * @example\n * ```ts\n * import { tool } from \"@langchain/core/tools\";\n * import { createAgent, providerToolSearchMiddleware } from \"langchain\";\n * import { ChatAnthropic } from \"@langchain/anthropic\";\n *\n * // A tool marked `defer_loading` at construction is deferred on its own —\n * // no need to list it in `searchableTools`; the middleware honors the flag.\n * const sendEmail = tool(sendEmailFn, {\n * name: \"send_email\",\n * description: \"Send an email\",\n * schema: sendEmailSchema,\n * extras: { defer_loading: true },\n * });\n *\n * const agent = createAgent({\n * model: new ChatAnthropic({ model: \"claude-sonnet-4-5\" }),\n * tools: [getWeather, sendEmail],\n * middleware: [providerToolSearchMiddleware()],\n * });\n * ```\n *\n * @param config - Configuration options for the middleware\n * @param config.searchableTools - Tools to defer behind tool search\n * @returns A middleware instance that can be used with `createAgent`\n */\nexport function providerToolSearchMiddleware(\n config: ProviderToolSearchMiddlewareConfig = {}\n) {\n const deferNames = toToolNames(config.searchableTools);\n\n return createMiddleware({\n name: \"ProviderToolSearch\",\n wrapModelCall: (request, handler) => {\n const tools = request.tools ?? [];\n\n // Fail fast if we try to defer a tool that is not bound to the model\n if (deferNames.size > 0) {\n const available = tools.filter(isLangChainTool).map((t) => t.name);\n const unknown = [...deferNames].filter(\n (name) => !available.includes(name)\n );\n if (unknown.length > 0) {\n throw new Error(\n `providerToolSearchMiddleware: searchableTools references tool(s) not bound to the model: ${unknown.join(\", \")}`\n );\n }\n }\n\n const provider = getModelProvider(request.model);\n if (!supportsProviderToolSearch(provider)) {\n throw new Error(\n `providerToolSearchMiddleware requires a provider with server-side tool search, but got ${provider}`\n );\n }\n\n // Nothing to defer -> pass thru\n if (!hasDeferredTools(tools, deferNames)) return handler(request);\n\n // For each deferred tool, emit a minimal binding spec carrying `defer_loading`.\n const boundTools = tools.map((tool) =>\n deferToolIfNeeded(tool, deferNames)\n );\n\n const nativeSearchTool = SERVER_TOOL_SEARCH_TOOLS[provider];\n return handler({ ...request, tools: [...boundTools, nativeSearchTool] });\n },\n });\n}\n\nfunction isDeferred(\n tool: unknown,\n deferNames: Set<string>\n): tool is StructuredToolParams {\n return (\n isLangChainTool(tool) &&\n (tool.extras?.defer_loading === true || deferNames.has(tool.name))\n );\n}\n\nfunction hasDeferredTools(\n tools: readonly (ClientTool | ServerTool)[],\n deferNames: Set<string>\n): boolean {\n return tools.some((tool) => isDeferred(tool, deferNames));\n}\n\n/**\n * If a tool should be deferred, return a minimal binding spec carrying `defer_loading`.\n * Otherwise return it as-is\n */\nfunction deferToolIfNeeded(\n tool: ClientTool | ServerTool,\n deferNames: Set<string>\n): ClientTool | ServerTool {\n if (!isDeferred(tool, deferNames)) return tool;\n return {\n name: tool.name,\n description: tool.description,\n schema: tool.schema,\n extras: { ...tool.extras, defer_loading: true },\n };\n}\n\n/** Flatten a list of tool names/instances into a set of tool names. */\nfunction toToolNames(tools: ToolIdentifier[] = []): Set<string> {\n return new Set(tools.map((t) => (typeof t === \"string\" ? t : t.name)));\n}\n\nfunction getModelProvider(model: LanguageModelLike): DetectedProvider {\n const name = model.getName();\n const configured =\n name === \"ConfigurableModel\"\n ? (model as ConfigurableModel)._defaultConfig?.modelProvider\n : undefined;\n if (name === \"ChatAnthropic\" || configured === \"anthropic\")\n return \"anthropic\";\n if (name === \"ChatOpenAI\" || configured === \"openai\") return \"openai\";\n return \"other\";\n}\n\nfunction supportsProviderToolSearch(\n provider: DetectedProvider\n): provider is ServerSearchCapableProvider {\n return (SERVER_SEARCH_PROVIDERS as readonly string[]).includes(provider);\n}\n"],"mappings":";;;AA4BA,MAAM,0BAA0B,CAC9B,aACA,QACF;AAOA,MAAM,2BAA2B;CAC/B,WAAW;EACT,MAAM;EACN,MAAM;CACR;CACA,QAAQ,EAAE,MAAM,cAAc;AAChC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDA,SAAgB,6BACd,SAA6C,CAAC,GAC9C;CACA,MAAM,aAAa,YAAY,OAAO,eAAe;CAErD,OAAO,iBAAiB;EACtB,MAAM;EACN,gBAAgB,SAAS,YAAY;GACnC,MAAM,QAAQ,QAAQ,SAAS,CAAC;GAGhC,IAAI,WAAW,OAAO,GAAG;IACvB,MAAM,YAAY,MAAM,OAAO,eAAe,CAAC,CAAC,KAAK,MAAM,EAAE,IAAI;IACjE,MAAM,UAAU,CAAC,GAAG,UAAU,CAAC,CAAC,QAC7B,SAAS,CAAC,UAAU,SAAS,IAAI,CACpC;IACA,IAAI,QAAQ,SAAS,GACnB,MAAM,IAAI,MACR,4FAA4F,QAAQ,KAAK,IAAI,GAC/G;GAEJ;GAEA,MAAM,WAAW,iBAAiB,QAAQ,KAAK;GAC/C,IAAI,CAAC,2BAA2B,QAAQ,GACtC,MAAM,IAAI,MACR,0FAA0F,UAC5F;GAIF,IAAI,CAAC,iBAAiB,OAAO,UAAU,GAAG,OAAO,QAAQ,OAAO;GAGhE,MAAM,aAAa,MAAM,KAAK,SAC5B,kBAAkB,MAAM,UAAU,CACpC;GAEA,MAAM,mBAAmB,yBAAyB;GAClD,OAAO,QAAQ;IAAE,GAAG;IAAS,OAAO,CAAC,GAAG,YAAY,gBAAgB;GAAE,CAAC;EACzE;CACF,CAAC;AACH;AAEA,SAAS,WACP,MACA,YAC8B;CAC9B,OACE,gBAAgB,IAAI,MACnB,KAAK,QAAQ,kBAAkB,QAAQ,WAAW,IAAI,KAAK,IAAI;AAEpE;AAEA,SAAS,iBACP,OACA,YACS;CACT,OAAO,MAAM,MAAM,SAAS,WAAW,MAAM,UAAU,CAAC;AAC1D;;;;;AAMA,SAAS,kBACP,MACA,YACyB;CACzB,IAAI,CAAC,WAAW,MAAM,UAAU,GAAG,OAAO;CAC1C,OAAO;EACL,MAAM,KAAK;EACX,aAAa,KAAK;EAClB,QAAQ,KAAK;EACb,QAAQ;GAAE,GAAG,KAAK;GAAQ,eAAe;EAAK;CAChD;AACF;;AAGA,SAAS,YAAY,QAA0B,CAAC,GAAgB;CAC9D,OAAO,IAAI,IAAI,MAAM,KAAK,MAAO,OAAO,MAAM,WAAW,IAAI,EAAE,IAAK,CAAC;AACvE;AAEA,SAAS,iBAAiB,OAA4C;CACpE,MAAM,OAAO,MAAM,QAAQ;CAC3B,MAAM,aACJ,SAAS,sBACJ,MAA4B,gBAAgB,gBAC7C,KAAA;CACN,IAAI,SAAS,mBAAmB,eAAe,aAC7C,OAAO;CACT,IAAI,SAAS,gBAAgB,eAAe,UAAU,OAAO;CAC7D,OAAO;AACT;AAEA,SAAS,2BACP,UACyC;CACzC,OAAQ,wBAA8C,SAAS,QAAQ;AACzE"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
require("../../_virtual/_rolldown/runtime.cjs");
|
|
2
1
|
const require_chat_models_universal = require("../../chat_models/universal.cjs");
|
|
3
2
|
const require_utils = require("../utils.cjs");
|
|
4
3
|
const require_utils$1 = require("./utils.cjs");
|
|
5
4
|
const require_middleware = require("../middleware.cjs");
|
|
5
|
+
const require_constants = require("./constants.cjs");
|
|
6
6
|
let _langchain_core_messages = require("@langchain/core/messages");
|
|
7
7
|
let _langchain_core_runnables = require("@langchain/core/runnables");
|
|
8
8
|
let _langchain_langgraph = require("@langchain/langgraph");
|
|
@@ -42,7 +42,6 @@ Messages to summarize:
|
|
|
42
42
|
const DEFAULT_SUMMARY_PREFIX = "Here is a summary of the conversation to date:";
|
|
43
43
|
const DEFAULT_MESSAGES_TO_KEEP = 20;
|
|
44
44
|
const DEFAULT_TRIM_TOKEN_LIMIT = 4e3;
|
|
45
|
-
const DEFAULT_FALLBACK_MESSAGE_COUNT = 15;
|
|
46
45
|
const SEARCH_RANGE_FOR_TOOL_PAIRS = 5;
|
|
47
46
|
const tokenCounterSchema = zod_v3.z.function().args(zod_v3.z.array(zod_v3.z.custom())).returns(zod_v3.z.union([zod_v3.z.number(), zod_v3.z.promise(zod_v3.z.number())]));
|
|
48
47
|
const contextSizeSchema = zod_v3.z.object({
|
|
@@ -262,7 +261,7 @@ model: zod_v3.z.custom().optional() }),
|
|
|
262
261
|
const { messagesToSummarize, preservedMessages } = partitionMessages(systemPrompt, conversationMessages, cutoffIndex);
|
|
263
262
|
const summaryMessage = new _langchain_core_messages.HumanMessage({
|
|
264
263
|
content: `${summaryPrefix}\n\n${await createSummary(messagesToSummarize, model, summaryPrompt, tokenCounter, trimTokensToSummarize, runtime)}`,
|
|
265
|
-
id:
|
|
264
|
+
id: conversationMessages[0].id,
|
|
266
265
|
additional_kwargs: { lc_source: "summarization" }
|
|
267
266
|
});
|
|
268
267
|
return { messages: [
|
|
@@ -535,7 +534,10 @@ async function createSummary(messagesToSummarize, model, summaryPrompt, tokenCou
|
|
|
535
534
|
const formattedMessages = (0, _langchain_core_messages.getBufferString)(trimmedMessages);
|
|
536
535
|
try {
|
|
537
536
|
const formattedPrompt = summaryPrompt.replace("{messages}", formattedMessages);
|
|
538
|
-
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
|
+
});
|
|
539
541
|
const content = (await model.invoke(formattedPrompt, config)).content;
|
|
540
542
|
/**
|
|
541
543
|
* Handle both string content and MessageContent array
|
|
@@ -568,10 +570,11 @@ async function trimMessagesForSummary(messages, tokenCounter, trimTokensToSummar
|
|
|
568
570
|
/**
|
|
569
571
|
* Fallback to last N messages if trimming fails
|
|
570
572
|
*/
|
|
571
|
-
return messages.slice(-
|
|
573
|
+
return messages.slice(-15);
|
|
572
574
|
}
|
|
573
575
|
}
|
|
574
576
|
//#endregion
|
|
577
|
+
exports.DEFAULT_SUMMARY_PROMPT = DEFAULT_SUMMARY_PROMPT;
|
|
575
578
|
exports.contextSizeSchema = contextSizeSchema;
|
|
576
579
|
exports.getProfileLimits = getProfileLimits;
|
|
577
580
|
exports.keepSchema = keepSchema;
|