UNPKG

@tanstack/ai

Version:

Core TanStack AI library - Open source AI SDK

225 lines (211 loc) 6.27 kB
import type { StandardJSONSchemaV1 } from '@standard-schema/spec' import type { InferSchemaType, JSONSchema, SchemaInput, Tool, ToolExecutionContext, } from '../../../types' /** * Marker type for server-side tools */ export interface ServerTool< TInput extends SchemaInput = SchemaInput, TOutput extends SchemaInput = SchemaInput, TName extends string = string, > extends Tool<TInput, TOutput, TName> { __toolSide: 'server' } /** * Marker type for client-side tools */ export interface ClientTool< TInput extends SchemaInput = SchemaInput, TOutput extends SchemaInput = SchemaInput, TName extends string = string, > { __toolSide: 'client' name: TName description: string inputSchema?: TInput outputSchema?: TOutput needsApproval?: boolean lazy?: boolean metadata?: Record<string, unknown> execute?: ( args: InferSchemaType<TInput>, ) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput> } /** * Tool definition that can be used directly or instantiated for server/client */ export interface ToolDefinitionInstance< TInput extends SchemaInput = SchemaInput, TOutput extends SchemaInput = SchemaInput, TName extends string = string, > extends Tool<TInput, TOutput, TName> { __toolSide: 'definition' } /** * Union type for any kind of client-side tool (client tool or definition) */ export type AnyClientTool = | ClientTool<SchemaInput, SchemaInput> | ToolDefinitionInstance<SchemaInput, SchemaInput> /** * Extract the tool name as a literal type */ export type InferToolName<T> = T extends { name: infer N } ? N : never /** * Extract the input type from a tool (inferred from Standard JSON Schema, or `unknown` for plain JSONSchema) */ export type InferToolInput<T> = T extends { inputSchema?: infer TInput } ? TInput extends StandardJSONSchemaV1<infer TInferred, unknown> ? TInferred : TInput extends JSONSchema ? unknown : unknown : unknown /** * Extract the output type from a tool (inferred from Standard JSON Schema, or `unknown` for plain JSONSchema) */ export type InferToolOutput<T> = T extends { outputSchema?: infer TOutput } ? TOutput extends StandardJSONSchemaV1<infer TInferred, unknown> ? TInferred : TOutput extends JSONSchema ? unknown : unknown : unknown /** * Tool definition configuration */ export interface ToolDefinitionConfig< TInput extends SchemaInput = SchemaInput, TOutput extends SchemaInput = SchemaInput, TName extends string = string, > { name: TName description: string inputSchema?: TInput outputSchema?: TOutput needsApproval?: boolean lazy?: boolean metadata?: Record<string, unknown> } /** * Tool definition builder that allows creating server or client tools from a shared definition */ export interface ToolDefinition< TInput extends SchemaInput = SchemaInput, TOutput extends SchemaInput = SchemaInput, TName extends string = string, > extends ToolDefinitionInstance<TInput, TOutput, TName> { /** * Create a server-side tool with execute function */ server: ( execute: ( args: InferSchemaType<TInput>, context?: ToolExecutionContext, ) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput>, ) => ServerTool<TInput, TOutput, TName> /** * Create a client-side tool with optional execute function */ client: ( execute?: ( args: InferSchemaType<TInput>, ) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput>, ) => ClientTool<TInput, TOutput, TName> } /** * Create an isomorphic tool definition that can be used directly or instantiated for server/client * * The definition contains all tool metadata (name, description, schemas) and can be: * 1. Used directly in chat() on the server (as a tool definition without execute) * 2. Instantiated as a server tool with .server() * 3. Instantiated as a client tool with .client() * * Supports any Standard JSON Schema compliant library (Zod v4+, ArkType, Valibot, etc.) * or plain JSON Schema objects. * * @example * ```typescript * import { toolDefinition } from '@tanstack/ai'; * import { z } from 'zod'; * * // Using Zod (natively supports Standard JSON Schema) * const addToCartTool = toolDefinition({ * name: 'addToCart', * description: 'Add a guitar to the shopping cart (requires approval)', * needsApproval: true, * inputSchema: z.object({ * guitarId: z.string(), * quantity: z.number(), * }), * outputSchema: z.object({ * success: z.boolean(), * cartId: z.string(), * totalItems: z.number(), * }), * }); * * // Use directly in chat (server-side, no execute function) * chat({ * tools: [addToCartTool], * // ... * }); * * // Or create server-side implementation * const addToCartServer = addToCartTool.server(async (args) => { * // args is typed as { guitarId: string; quantity: number } * return { * success: true, * cartId: 'CART_' + Date.now(), * totalItems: args.quantity, * }; * }); * * // Or create client-side implementation * const addToCartClient = addToCartTool.client(async (args) => { * // Client-specific logic (e.g., localStorage) * return { success: true, cartId: 'local', totalItems: 1 }; * }); * ``` */ export function toolDefinition< TInput extends SchemaInput = SchemaInput, TOutput extends SchemaInput = SchemaInput, TName extends string = string, >( config: ToolDefinitionConfig<TInput, TOutput, TName>, ): ToolDefinition<TInput, TOutput, TName> { const definition: ToolDefinition<TInput, TOutput, TName> = { __toolSide: 'definition', ...config, server( execute: ( args: InferSchemaType<TInput>, context?: ToolExecutionContext, ) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput>, ): ServerTool<TInput, TOutput, TName> { return { __toolSide: 'server', ...config, execute, } }, client( execute?: ( args: InferSchemaType<TInput>, ) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput>, ): ClientTool<TInput, TOutput, TName> { return { __toolSide: 'client', ...config, execute, } }, } return definition }