Skip to content

Tool Calling ​

Declare tools with JSON Schema parameters. A tool with execute runs automatically, its return value goes back to the model, and the conversation continues.

ts
import { defineTool, useChat } from 'vue-llm'

const getWeather = defineTool<{ city: string }, { temp: number }>({
  name: 'get_weather',
  description: 'Get the current weather for a city',
  parameters: {
    type: 'object',
    properties: { city: { type: 'string' } },
    required: ['city'],
  },
  execute: ({ city }, { signal }) => fetch(`/api/weather?city=${city}`, { signal }).then(r => r.json()),
})

const { messages, sendMessage } = useChat({
  provider: ai,
  model: 'gpt-4o',
  tools: [getWeather],
  maxToolRoundtrips: 3,
})

defineTool only adds types. execute receives the parsed arguments and a signal that aborts when the chat is stopped. If it throws, the error message is sent to the model so it can recover.

onToolCall ​

Tools without execute go to onToolCall, a single handler for every tool:

ts
useChat({
  provider: ai,
  model: 'gpt-4o',
  tools: [{ name: 'get_weather', description: '…', parameters: { /* … */ } }],
  async onToolCall({ toolName, args }) {
    if (toolName === 'get_weather') return fetchWeather(args as { city: string })
  },
})

Lifecycle ​

Each call appears as a tool-call part on the assistant message:

stateMeaning
streamingArguments are arriving. args holds what has been parsed so far.
pendingArguments are complete and the call is waiting for a result
runningexecute / onToolCall is in progress
resultresult holds the return value
errorThe tool threw; error holds the message

Human in the loop ​

A call that gets no result (no execute, and onToolCall is missing or returns undefined) pauses the chat in pending. Resolve it from your UI with addToolResult. Once no call is left pending, the chat sends the results and continues:

ts
await addToolResult({ toolCallId, result: { approved: true } })
await addToolResult({ toolCallId, error: 'The user declined' })

Set autoSendToolResults: false to only record the result. If the user sends a new message instead, unanswered calls are reported to the model as not answered.

To render these calls as components, see Generative UI.

maxToolRoundtrips (default 5) caps how many times the model may call tools in response to one message.

Server-side tools ​

Tools that need secrets or database access should run on the server. With createLLMHandler add execute to the tool; with your own backend emit tool-result chunks via createSSEProvider. Either way the part's state becomes result and the client makes no extra request.

Released under the MIT License.