UNPKG

langchain

Version:
1 lines 7.39 kB
{"version":3,"file":"headless.cjs","names":[],"sources":["../../src/tools/headless.ts"],"sourcesContent":["/**\n * Unified Tool Primitive for LangChain Agents\n *\n * This module re-exports the `tool` primitive from `@langchain/core/tools` with\n * an additional overload: when called without an implementation function, it\n * creates a **headless tool** that interrupts agent execution and delegates the\n * implementation to the client (e.g. via `useStream({ tools: [...] })`).\n *\n * @module\n */\n\nimport {\n tool as coreTool,\n DynamicStructuredTool,\n type ToolRunnableConfig,\n} from \"@langchain/core/tools\";\nimport type {\n InteropZodObject,\n InferInteropZodInput,\n InferInteropZodOutput,\n} from \"@langchain/core/utils/types\";\n\n/**\n * Configuration fields for creating a headless tool.\n */\nexport type HeadlessToolFields<\n SchemaT extends InteropZodObject,\n NameT extends string = string,\n> = {\n /** The name of the tool. Used by the client to match implementations. */\n name: NameT;\n /** Description of what the tool does. */\n description: string;\n /** The Zod schema defining the tool's input. */\n schema: SchemaT;\n};\n\n/**\n * A tool implementation that pairs a headless tool with its execution function.\n *\n * Created by calling `.implement()` on a {@link HeadlessTool}.\n * Pass to `useStream({ tools: [...] })` on the client side.\n */\nexport type HeadlessToolImplementation<\n SchemaT extends InteropZodObject = InteropZodObject,\n OutputT = unknown,\n NameT extends string = string,\n> = {\n tool: HeadlessTool<SchemaT, NameT>;\n execute: (args: InferInteropZodOutput<SchemaT>) => Promise<OutputT>;\n};\n\n/**\n * A headless tool that always interrupts agent execution on the server.\n *\n * The implementation is provided separately on the client via\n * `useStream({ tools: [...] })` using `.implement()`.\n */\nexport type HeadlessTool<\n SchemaT extends InteropZodObject = InteropZodObject,\n NameT extends string = string,\n> = DynamicStructuredTool<\n SchemaT,\n InferInteropZodOutput<SchemaT>,\n InferInteropZodInput<SchemaT>,\n unknown,\n unknown,\n NameT\n> & {\n /**\n * Pairs this headless tool with a client-side implementation.\n *\n * The returned object should be passed to `useStream({ tools: [...] })`.\n * The SDK matches the implementation to the tool by name and calls\n * `execute` with the typed arguments from the interrupt payload.\n *\n * @param execute - The function that implements the tool on the client\n */\n implement: <OutputT>(\n execute: (args: InferInteropZodOutput<SchemaT>) => Promise<OutputT>\n ) => HeadlessToolImplementation<SchemaT, OutputT, NameT>;\n};\n\nfunction createHeadlessTool<\n SchemaT extends InteropZodObject,\n NameT extends string,\n>(fields: HeadlessToolFields<SchemaT, NameT>): HeadlessTool<SchemaT, NameT> {\n const { name, description, schema } = fields;\n\n const wrappedTool = coreTool(\n async (\n args: InferInteropZodOutput<SchemaT>,\n config?: ToolRunnableConfig\n ) => {\n const { interrupt } = await import(\"@langchain/langgraph\");\n return interrupt({\n type: \"tool\",\n toolCall: {\n id: config?.toolCall?.id,\n name,\n args,\n },\n });\n },\n {\n name,\n description,\n schema,\n metadata: {\n headlessTool: true,\n },\n }\n );\n\n const headlessTool: HeadlessTool<SchemaT, NameT> = Object.assign(\n wrappedTool,\n {\n implement: <OutputT>(\n execute: (args: InferInteropZodOutput<SchemaT>) => Promise<OutputT>\n ): HeadlessToolImplementation<SchemaT, OutputT, NameT> => ({\n tool: headlessTool,\n execute,\n }),\n }\n ) as HeadlessTool<SchemaT, NameT>;\n\n return headlessTool;\n}\n\n/**\n * The headless overload signature added to the core `tool` function.\n *\n * When called **without** an implementation function — just `tool({ name, description, schema })` —\n * returns a {@link HeadlessTool} that interrupts on every agent invocation.\n * The client provides the implementation via `useStream({ tools: [...] })`.\n */\ntype HeadlessToolOverload = {\n <SchemaT extends InteropZodObject, NameT extends string>(\n fields: HeadlessToolFields<SchemaT, NameT>\n ): HeadlessTool<SchemaT, NameT>;\n};\n\n/**\n * Unified tool primitive for LangChain agents.\n *\n * Enhances the `tool` function from `@langchain/core/tools` with a headless\n * overload: when called **without** an implementation function, the tool\n * interrupts agent execution and lets the client supply the implementation.\n *\n * ---\n *\n * **Normal tool** — pass an implementation function as the first argument:\n *\n * ```typescript\n * import { tool } from \"langchain/tools\";\n * import { z } from \"zod\";\n *\n * const getWeather = tool(\n * async ({ city }) => `The weather in ${city} is sunny.`,\n * {\n * name: \"get_weather\",\n * description: \"Get the weather for a city\",\n * schema: z.object({ city: z.string() }),\n * }\n * );\n * ```\n *\n * ---\n *\n * **Headless tool** — omit the implementation; the client provides it later:\n *\n * ```typescript\n * import { tool } from \"langchain/tools\";\n * import { z } from \"zod\";\n *\n * // Server: define the tool shape — no implementation needed\n * export const getLocation = tool({\n * name: \"get_location\",\n * description: \"Get the user's current GPS location\",\n * schema: z.object({\n * highAccuracy: z.boolean().optional().describe(\"Request high accuracy GPS\"),\n * }),\n * });\n *\n * // Server: register with the agent\n * const agent = createAgent({\n * model: \"openai:gpt-4o\",\n * tools: [getLocation],\n * });\n *\n * // Client: provide the implementation in useStream\n * const stream = useStream({\n * assistantId: \"agent\",\n * tools: [\n * getLocation.implement(async ({ highAccuracy }) => {\n * return new Promise((resolve, reject) => {\n * navigator.geolocation.getCurrentPosition(\n * (pos) => resolve({\n * latitude: pos.coords.latitude,\n * longitude: pos.coords.longitude,\n * }),\n * (err) => reject(new Error(err.message)),\n * { enableHighAccuracy: highAccuracy }\n * );\n * });\n * }),\n * ],\n * });\n * ```\n */\nexport const tool: HeadlessToolOverload & typeof coreTool = ((\n funcOrFields: unknown,\n fields?: unknown\n) => {\n if (typeof funcOrFields !== \"function\") {\n return createHeadlessTool(\n funcOrFields as HeadlessToolFields<InteropZodObject, string>\n );\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (coreTool as any)(funcOrFields, fields);\n}) as HeadlessToolOverload & typeof coreTool;\n"],"mappings":";;;;;;;;;;;;;AAmFA,SAAS,mBAGP,QAA0E;CAC1E,MAAM,EAAE,MAAM,aAAa,WAAW;CAEtC,MAAM,eAAA,GAAA,sBAAA,MACJ,OACE,MACA,WACG;EACH,MAAM,EAAE,cAAc,MAAM,OAAO;AACnC,SAAO,UAAU;GACf,MAAM;GACN,UAAU;IACR,IAAI,QAAQ,UAAU;IACtB;IACA;IACD;GACF,CAAC;IAEJ;EACE;EACA;EACA;EACA,UAAU,EACR,cAAc,MACf;EACF,CACF;CAED,MAAM,eAA6C,OAAO,OACxD,aACA,EACE,YACE,aACyD;EACzD,MAAM;EACN;EACD,GACF,CACF;AAED,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoFT,MAAa,SACX,cACA,WACG;AACH,KAAI,OAAO,iBAAiB,WAC1B,QAAO,mBACL,aACD;AAGH,SAAA,GAAA,sBAAA,MAAyB,cAAc,OAAO"}