Function calling
A call intent lets a model fill arguments for an existing function. Nola resolves all extractor arguments in one provider call, validates them, then invokes the function with those values.
The helper
Section titled “The helper”Start with a TypeScript function that creates a ticket:
export async function createTicket(title: string, priority: number): Promise<string> { const res = await fetch("https://example.test/tickets", { method: "POST", body: JSON.stringify({ title, priority }), }); return (await res.json()).id as string;}The call intent
Section titled “The call intent”Import the helper into a .tsi file and use an extractor for the argument the model should supply. This example keeps the priority fixed at 2:
import { createTicket } from "./tickets.js";
export infer function fileTicket(.request: string) { // Sigil-less: the extractor argument makes this call an intent. `2` is a // plain argument and is passed through untouched. const id = ask createTicket(`a short ticket title for the request`: string, 2); return id;}The call intent awaits createTicket, so id has type string. You do not need another await around the ask.
Adding instruction text
Section titled “Adding instruction text”To give the model an instruction for the whole call, put a template literal after the function name:
import { createTicket } from "./tickets.js";
export infer function fileTicketCarefully(.request: string) { return ask createTicket`file the ticket exactly as the customer described it`( `a short ticket title`: string, `priority 1-5, where 1 is most urgent`: number, );}Every extractor argument needs an explicit type; an untyped template in the argument list is a plain string, not an extractor, and an untyped ..`…` slot raises NOLA2004. The model receives the function name, each extractor’s type and instruction, and the call hint. It fills the extractor arguments together in one response. Plain arguments are passed to the function without being shown to the model.
Idempotency
Section titled “Idempotency”The function runs as part of resolving the ask. If it runs and then the attempt fails, .withRetry(n) can run it again. Use intent retries only when repeated calls are safe.
To keep the helper outside each extraction’s retry loop, extract the arguments first and call it separately:
import { createTicket } from "./tickets.js";
export infer function fileTicketOnce(.request: string) { const title = ask `a short ticket title for the request`: string; const priority = ask `priority 1-5, where 1 is most urgent`: number; return await createTicket(title, priority); // plain await — runs exactly once}The helper runs once per execution of this function body. Retrying the whole fileTicketOnce intent can still repeat it, so splitting the asks does not guarantee exactly-once side effects.
Timeouts
Section titled “Timeouts”ask.timeoutMs and .withTimeout limit provider round trips. They do not cancel a function that has already started running. Give the helper its own timeout or cancellation handling if it needs one. See Ask options.
Calling from plain TypeScript
Section titled “Calling from plain TypeScript”import { fileTicket } from "./file-ticket.tsi";
const id = await fileTicket("Checkout is down for every customer in the EU — please treat as urgent.");console.log("created", id);See Call intents for all three spellings and the rules that distinguish an intent from a plain function call.
Next: Testing