{"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"}