Skip to Content
APIHooksuseAIFrontendTool

useAIFrontendTool

The useAIFrontendTool hook allows you to register functions that the AI can execute in your React components. This enables direct interaction with your UI and state, creating powerful experiences where the AI can manipulate widgets, update data, and trigger actions based on user requests.

Syntax

interface FrontendTool { name: string; description: string; parameters: ZodSchema; execute: (params: any) => Promise<any> | any; } function useAIFrontendTool(tool: FrontendTool): void

Parameters

PropertyTypeRequiredDescription
namestringYesUnique tool identifier
descriptionstringYesClear description for the AI to understand when to use the tool
parametersZodSchemaYesZod schema defining the tool’s parameters
execute(params: any) => Promise<any> | anyYesFunction that executes when the AI calls the tool

Tool Registration

Basic Example

import { useAIFrontendTool } from "ai-chat-bootstrap"; import { z } from "zod"; function CounterWidget() { const [counter, setCounter] = useState(0); useAIFrontendTool({ name: "increment_counter", description: "Increment the counter by a specified amount", parameters: z.object({ amount: z.number().default(1).describe("Amount to increment by"), }), execute: async (params: { amount: number }) => { const newValue = counter + params.amount; setCounter(newValue); return { newValue, amount: params.amount, message: `Counter incremented by ${params.amount}. New value: ${newValue}` }; }, }); return <div>Counter: {counter}</div>; }

Multiple Tools in One Component

function CounterControls() { const [counter, setCounter] = useState(0); // Increment tool useAIFrontendTool({ name: "increment_counter", description: "Increment the counter by a specified amount", parameters: z.object({ amount: z.number().default(1).describe("Amount to increment by"), }), execute: async (params: { amount: number }) => { const newValue = counter + params.amount; setCounter(newValue); return { newValue, amount: params.amount, message: `Counter incremented by ${params.amount}. New value: ${newValue}` }; }, }); // Decrement tool useAIFrontendTool({ name: "decrement_counter", description: "Decrement the counter by a specified amount", parameters: z.object({ amount: z.number().default(1).describe("Amount to decrement by"), }), execute: async (params: { amount: number }) => { const newValue = counter - params.amount; setCounter(newValue); return { newValue, amount: params.amount, message: `Counter decremented by ${params.amount}. New value: ${newValue}` }; }, }); // Reset tool useAIFrontendTool({ name: "reset_counter", description: "Reset the counter to zero", parameters: z.object({}), execute: async () => { setCounter(0); return { newValue: 0, message: "Counter reset to 0" }; }, }); return <div>Counter: {counter}</div>; }

Parameter Validation

Tools use Zod schemas for type-safe parameter validation:

Basic Types

parameters: z.object({ amount: z.number().min(1).max(100).describe("Amount to increment by"), reason: z.string().optional().describe("Optional reason for the increment"), enabled: z.boolean().default(true).describe("Whether to enable the feature"), })

Complex Types

parameters: z.object({ user: z.object({ id: z.string().describe("User ID"), name: z.string().describe("User name"), email: z.string().email().describe("User email address"), }), settings: z.object({ theme: z.enum(["light", "dark", "auto"]).describe("UI theme"), notifications: z.boolean().describe("Enable notifications"), }).optional(), tags: z.array(z.string()).describe("Array of tags"), })

Validation with Defaults

parameters: z.object({ amount: z.number().default(1).describe("Amount to change"), direction: z.enum(["up", "down"]).default("up").describe("Direction to change"), animate: z.boolean().default(true).describe("Whether to animate the change"), })

Return Values

Tools can return structured data that the AI uses for context:

Simple Response

execute: async (params) => { const result = performAction(params); return { success: true, value: result, message: "Operation completed successfully" }; }

Detailed Response

execute: async (params) => { const startTime = Date.now(); const result = await performComplexOperation(params); const duration = Date.now() - startTime; return { success: true, result: result.data, metadata: { duration: `${duration}ms`, itemsProcessed: result.count, timestamp: new Date().toISOString(), }, message: `Processed ${result.count} items in ${duration}ms`, nextSteps: ["Review results", "Save changes", "Notify users"] }; }

Error Handling

Tools should handle errors gracefully:

execute: async (params) => { try { // Validation if (params.amount < 0) { return { success: false, error: "Amount cannot be negative", message: "Please provide a positive amount" }; } // Perform operation const result = await riskyOperation(params); return { success: true, result, message: "Operation completed successfully" }; } catch (error) { console.error("Tool execution failed:", error); return { success: false, error: error.message, message: "Operation failed, please try again", details: process.env.NODE_ENV === 'development' ? error.stack : undefined }; } }

Complete Integration Example

"use client"; import React, { useState } from "react"; import { ChatContainer, useAIChat, useAIFrontendTool } from "ai-chat-bootstrap"; import { z } from "zod"; export function ChatWithTools() { const [counter, setCounter] = useState(0); // Register frontend tools that AI can use useAIFrontendTool({ name: "increment_counter", description: "Increment the counter by a specified amount", parameters: z.object({ amount: z.number().default(1).describe("Amount to increment by"), }), execute: async (params: { amount: number }) => { const newValue = counter + params.amount; setCounter(newValue); return { newValue, amount: params.amount, message: `Counter incremented by ${params.amount}. New value: ${newValue}` }; }, }); const chat = useAIChat({ api: "/api/chat", systemPrompt: "You are a helpful assistant that can control a counter widget. Use the increment_counter tool when users ask you to change the counter value." }); return ( <div className="space-y-4"> {/* Counter Display */} <div className="flex items-center justify-center p-6 border rounded-lg bg-muted/50"> <div className="text-center"> <div className="text-4xl font-bold text-primary mb-2">{counter}</div> <div className="text-sm text-muted-foreground">Counter Value</div> </div> </div> {/* Chat Interface */} <div className="h-[420px] w-full"> <ChatContainer chat={chat} header={{ title: "AI Assistant with Tools", subtitle: "Can control the counter above" }} ui={{ placeholder: "Try: 'increment by 3' or 'decrease by 2'" }} /> </div> </div> ); }

Backend Integration

import { createAzure } from "@ai-sdk/azure"; import { convertToModelMessages, streamText } from "ai"; const azure = createAzure({ resourceName: process.env.AZURE_RESOURCE_NAME!, apiKey: process.env.AZURE_API_KEY!, apiVersion: process.env.AZURE_API_VERSION ?? "preview", }); const model = azure(process.env.AZURE_DEPLOYMENT_ID!); export async function POST(req: Request) { const { messages, enrichedSystemPrompt, tools } = await req.json(); const result = await streamText({ model, messages: [ { role: "system", content: enrichedSystemPrompt }, ...convertToModelMessages(messages), ], tools, }); return result.toUIMessageStreamResponse(); }

Advanced Examples

Form Management Tool

function FormManager() { const [formData, setFormData] = useState({}); const [errors, setErrors] = useState({}); useAIFrontendTool({ name: "update_form_field", description: "Update a specific field in the form", parameters: z.object({ field: z.string().describe("Field name to update"), value: z.any().describe("New value for the field"), }), execute: async (params) => { setFormData(prev => ({ ...prev, [params.field]: params.value })); // Clear any existing error for this field if (errors[params.field]) { setErrors(prev => { const newErrors = { ...prev }; delete newErrors[params.field]; return newErrors; }); } return { success: true, field: params.field, value: params.value, message: `Updated ${params.field} to: ${params.value}` }; }, }); return <div>...</div>; }

Data Fetching Tool

function DataManager() { const [data, setData] = useState([]); const [loading, setLoading] = useState(false); useAIFrontendTool({ name: "fetch_user_data", description: "Fetch user data from the API", parameters: z.object({ userId: z.string().describe("User ID to fetch data for"), includeMetadata: z.boolean().default(false).describe("Include metadata in response"), }), execute: async (params) => { try { setLoading(true); const response = await fetch(`/api/users/${params.userId}${ params.includeMetadata ? '?include=metadata' : '' }`); if (!response.ok) { throw new Error(`Failed to fetch user: ${response.statusText}`); } const userData = await response.json(); setData(prev => [...prev, userData]); return { success: true, user: userData, message: `Fetched data for user ${userData.name}`, metadata: params.includeMetadata ? userData.metadata : undefined }; } catch (error) { return { success: false, error: error.message, message: `Failed to fetch user data: ${error.message}` }; } finally { setLoading(false); } }, }); return <div>...</div>; }

How It Works

  1. Tool Registration: useAIFrontendTool registers tools in a global store
  2. Schema Serialization: Tool schemas are converted to JSON Schema for the backend
  3. AI Decision: The AI model decides when to call tools based on user input
  4. Execution: Tools execute in the browser and return results
  5. Context Update: Tool results are added to the conversation context
  6. UI Updates: Tools can modify React state, triggering re-renders

Best Practices

  1. Clear Descriptions: Write detailed tool descriptions so the AI knows when to use them
  2. Typed Parameters: Use specific Zod schemas for better validation
  3. Meaningful Returns: Return structured data that helps the AI understand what happened
  4. Error Handling: Handle failures gracefully and return error information
  5. State Management: Keep tool state in sync with your component state
  6. Performance: Avoid heavy computations in tool execution
  7. Security: Validate all parameters and sanitize inputs
  8. User Feedback: Provide clear feedback about tool execution results

TypeScript Interface

interface FrontendTool { name: string; description: string; parameters: ZodSchema; execute: (params: any) => Promise<any> | any; } interface ToolDefinition { type: "function"; function: { name: string; description: string; parameters: JSONSchema; }; }

Common Tool Patterns

  • State Updates: Modify component state based on AI requests
  • Form Management: Update form fields and validation
  • Data Fetching: Retrieve data from APIs
  • UI Actions: Trigger animations, modals, or navigation
  • File Operations: Read, write, or process files
  • External Integrations: Call external services or APIs

See Also

Last updated on