Function calling
When you already have a function that does something, do not extract its arguments one at a time and then call it. Make the call itself the intent: the model fills every extractor-shaped argument in one provider call, and the function runs with the results.
The helper
Section titled “The helper”Ordinary TypeScript, nothing Nola about it:
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”The .tsi imports the helper with the NodeNext .js specifier and gives the call extractor arguments. That is the whole recipe:
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;}createTicket is async, but id is a string, not a Promise<string>: a call intent awaits a promise-returning callee itself (ask ≈ await), so await ask createTicket(…) is redundant.
Adding instruction text
Section titled “Adding instruction text”The marker form is the only spelling that carries a hint for the call:
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 slot needs an explicit <T> (NOLA2004), and all slots of one call resolve together in a single provider round trip — the model sees the whole signature and fills the arguments consistently.
Idempotency
Section titled “Idempotency”Because the callee runs inside the ask, .withRetry(n) on a call intent re-invokes it on every failed attempt. Only use it when the target is idempotent. When it is not, split the work: extract the arguments with plain extractors, then call the function in ordinary TypeScript under your own retry policy.
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}Timeouts
Section titled “Timeouts”The invocation timeout (ask.timeoutMs / .withTimeout) bounds provider round trips only. Once the arguments are filled, the callee’s own promise runs to completion, exactly like a plain await createTicket() in your code — 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);The grammar in full — the three spellings, the detection rule, what stays a plain call — is on Call intents.
Next: Testing