our friendly logo that looks like a hashbrown character from an animated tv showhashbrown

Function Calling

Function calling with a Large Language Model (LLM) is a powerful feature that allows you to define functions that the LLM can call. The LLM will determine if and when a function is called.

There are many use cases for function calling. Here are a few that we've implemented in our React applications:

  • Providing data to the LLM from React state or a service.
  • Performing tasks on behalf of the user.
  • Dispatching actions that are AI scoped to perform tasks or provide suggestions.

Demo


Defining a Tool

Hashbrown provides the useTool hook from @hashbrownai/react for defining functions that the LLM can invoke.

import { useTool } from '@hashbrownai/react';

export default function Chat() {
  export const getUserTool = useTool({
    name: 'getUser',
    description: 'Get information about the current user',
    deps: [fetchUser],
    handler: async (abortSignal) => {
      // Replace with your user fetching logic
      return await fetchUser({ signal: abortSignal });
    },
  });
}

Let's break down the example above:

  • name: The name of the function that the LLM will call.
  • description: A description of what the function does. This is used by the LLM to determine if it should call the function.
  • handler: The function that will be called when the LLM invokes the function. This is where you can perform any logic you need, such as fetching data from a service or performing a task. The function is invoked with an AbortSignal and is expected to return a Promise of the Result.
  • deps: The dependencies referenced in the handler function, like you would pass to useCallback

The method signature for a handler is:

(abortSignal: AbortSignal) => Promise<Result>;

Tools with Arguments

Hashbrown's useTool hook enables React developers to define functions that require arguments by specifying the schema. The LLM will invoke the function with the specified arguments.

We'll be using Skillet - hashbrown's LLM-optimized schema language - for defining the function arguments. Let's look at an example function that enables the LLM to control the lights in our smart home client application.

import { useTool } from '@hashbrownai/react';
import { s } from '@hashbrownai/core';

export const controlLight = useTool({
  name: 'controlLight',
  description: 'Control a light',
  schema: s.object('Control light input', {
    lightId: s.string('The id of the light'),
    brightness: s.number('The brightness of the light'),
  }),
  handler: async (input, abortSignal) => {
    // Replace with your update logic
    return await updateLight(input.lightId, { brightness: input.brightness }, abortSignal);
  },
});

Let's review the code above.

  • name: The name of the function that the LLM will call.
  • description: A description of what the function does. This is used by the LLM to determine if it should call the function.
  • schema: The schema that defines the arguments that the function requires. This is where you can define the input parameters for the function using Skillet.
  • handler: The function that will be called when the LLM invokes the function. This is where you can perform any logic you need, such as fetching data from a service or performing a task. The function is invoked with the input arguments and an AbortSignal, and is expected to return a Promise of the Result.

In this example, we expect that the input will be an object with the properties lightId and brightness, which are defined in the schema.

Note that the input arguments are strongly-typed based on the provided schema.

The method signature for a handler is:

(input: Input, abortSignal: AbortSignal) => Promise<Result>;

Providing the Tools

The next step is to provide the tools when using hashbrown's React hooks-based APIs.

import React from 'react';
import { useChat } from '@hashbrownai/react';
import { s } from '@hashbrownai/core';
import { getUser, getLights, controlLight } from './useTools';

export function ChatComponent() {
  const chat = useChat({
    model: 'gpt-4.1',
    system: 'You are a helpful assistant that can answer questions and help with tasks',
    tools: [getUser, getLights, controlLight],
  });

  const sendMessage = (message: string) => {
    chat.sendMessage({ role: 'user', content: message });
  };

  // ... render chat UI, messages, etc.
}

Let's review the code above.

  • First, we define the tools array and pass it to the useChat hook.
  • We use the useTool hook to define the functions that the LLM can call.
  • The handler functions are defined to perform the necessary logic, such as fetching data from services or updating the state.
  • Finally, we can use the sendMessage method to send a message to the LLM, which can then invoke the defined functions as needed.

Conclusion

Function calling is a powerful feature that allows you to define functions that the LLM can invoke. By using hashbrown's useTool hook, you can easily define functions with or without arguments that the LLM can call.

Function Calling Demo Defining a Tool Tools with Arguments Providing the Tools Conclusion

LiveLoveApp provides secure, compliant, and reliable long-term support to enterprises. We are a group of engineers who are passionate about open source.

Enterprise Support

AI Engineering Sprint

Get your team up-to-speed on AI engineering with a one week AI engineering sprint. Includes a workshop on AI engineering with hashbrown and a few days with the hashbrown developer team to bring your AI ideas to life.

Long Term Support

Keep your hashbrown deployments running at peak performance with our Long Term Support. Includes an ongoing support retainer for direct access to the hashbrown developer team, SLA-backed issue resolution, and guided upgrades.

Consulting

LiveLoveApp provides hands-on engagement with our AI engineers for architecture reviews, custom integrations, proof-of-concept builds, performance tuning, and expert guidance on best practices.