{"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,SACD;AAOD,MAAM,2BAA2B;CAC/B,WAAW;EACT,MAAM;EACN,MAAM;EACP;CACD,QAAQ,EAAE,MAAM,eAAe;CAChC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDD,SAAgB,6BACd,SAA6C,EAAE,EAC/C;CACA,MAAM,aAAa,YAAY,OAAO,gBAAgB;AAEtD,QAAOA,mBAAAA,iBAAiB;EACtB,MAAM;EACN,gBAAgB,SAAS,YAAY;GACnC,MAAM,QAAQ,QAAQ,SAAS,EAAE;AAGjC,OAAI,WAAW,OAAO,GAAG;IACvB,MAAM,YAAY,MAAM,OAAOC,uCAAAA,gBAAgB,CAAC,KAAK,MAAM,EAAE,KAAK;IAClE,MAAM,UAAU,CAAC,GAAG,WAAW,CAAC,QAC7B,SAAS,CAAC,UAAU,SAAS,KAAK,CACpC;AACD,QAAI,QAAQ,SAAS,EACnB,OAAM,IAAI,MACR,4FAA4F,QAAQ,KAAK,KAAK,GAC/G;;GAIL,MAAM,WAAW,iBAAiB,QAAQ,MAAM;AAChD,OAAI,CAAC,2BAA2B,SAAS,CACvC,OAAM,IAAI,MACR,0FAA0F,WAC3F;AAIH,OAAI,CAAC,iBAAiB,OAAO,WAAW,CAAE,QAAO,QAAQ,QAAQ;GAGjE,MAAM,aAAa,MAAM,KAAK,SAC5B,kBAAkB,MAAM,WAAW,CACpC;GAED,MAAM,mBAAmB,yBAAyB;AAClD,UAAO,QAAQ;IAAE,GAAG;IAAS,OAAO,CAAC,GAAG,YAAY,iBAAiB;IAAE,CAAC;;EAE3E,CAAC;;AAGJ,SAAS,WACP,MACA,YAC8B;AAC9B,SAAA,GAAA,uCAAA,iBACkB,KAAK,KACpB,KAAK,QAAQ,kBAAkB,QAAQ,WAAW,IAAI,KAAK,KAAK;;AAIrE,SAAS,iBACP,OACA,YACS;AACT,QAAO,MAAM,MAAM,SAAS,WAAW,MAAM,WAAW,CAAC;;;;;;AAO3D,SAAS,kBACP,MACA,YACyB;AACzB,KAAI,CAAC,WAAW,MAAM,WAAW,CAAE,QAAO;AAC1C,QAAO;EACL,MAAM,KAAK;EACX,aAAa,KAAK;EAClB,QAAQ,KAAK;EACb,QAAQ;GAAE,GAAG,KAAK;GAAQ,eAAe;GAAM;EAChD;;;AAIH,SAAS,YAAY,QAA0B,EAAE,EAAe;AAC9D,QAAO,IAAI,IAAI,MAAM,KAAK,MAAO,OAAO,MAAM,WAAW,IAAI,EAAE,KAAM,CAAC;;AAGxE,SAAS,iBAAiB,OAA4C;CACpE,MAAM,OAAO,MAAM,SAAS;CAC5B,MAAM,aACJ,SAAS,sBACJ,MAA4B,gBAAgB,gBAC7C,KAAA;AACN,KAAI,SAAS,mBAAmB,eAAe,YAC7C,QAAO;AACT,KAAI,SAAS,gBAAgB,eAAe,SAAU,QAAO;AAC7D,QAAO;;AAGT,SAAS,2BACP,UACyC;AACzC,QAAQ,wBAA8C,SAAS,SAAS"}