Skip to content

The mental model

Start with these five ideas, then use the linked pages for syntax and examples.

1. A .tsi file is TypeScript plus two constructs

Section titled “1. A .tsi file is TypeScript plus two constructs”

A .tsi file uses TypeScript for types, functions and control flow. Nola adds syntax for inference, including infer function, ask and contextual parameters.

analyze.tsi
export infer function analyzeUserRequest(userId: string, .message: string) {
const ticketId = ask `ticket id mentioned in the message`: string;
const isFraud = ask `does the message look fraudulent`: boolean;
return { userId, ticketId, isFraud };
}

Calling an infer function returns an Intent<T> without running its body. An extractor such as ..`…`: T also creates a request without sending it to a model. You can build either value now and resolve it later. See Intents.

3. ask is to intents what await is to promises

Section titled “3. ask is to intents what await is to promises”

ask resolves an askable: an extractor, a call intent, or the intent returned by another infer function. It has the same precedence as await. There are two rules to keep in mind:

  • ask is legal only directly inside an infer function body or directly in the module body — not inside a plain function or a nested closure (NOLA2001). A top-level ask runs as its own <module> invocation.
  • From plain TypeScript you do not ask; you await the intent an infer function returns. A raw extractor or call intent is never bare-awaited — it has no context of its own and throws NOLA3010.

A parameter with a leading dot, such as .message, is a contextual parameter. Its value is included in the prompt for every ask in that invocation. A plain parameter such as userId stays available to your code; the prompt does not include it.

An extractor requests a typed answer: ask `instruction`: T. In a call intent the argument is written the same way, createTicket(`a short title`: string). When you store an extractor in a variable, add two dots: ..`instruction`: T.

The Nola toolchain compiles .tsi into plain TypeScript, much as a JSX transform converts markup into function calls. nola build, nola run, nola check and the editor integration share this transformation, called lowering. Your project needs the Nola tools to read .tsi; downstream tools then work with the generated TypeScript.

Term Meaning
infer function infer function name(…) { … } — an LLM-backed function; calling it returns an Intent<T>
contextual parameter .name: T — a parameter whose value is shown to the model
intent the lazy value ask (or await, from plain TS) resolves — see Intents
askable anything ask accepts: an extractor, a call intent, an infer function’s intent
extractor ask `instruction`: T (..`instruction`: T when stored, away from ask and a call’s arguments) — a request to pull a T from context
call intent fn`hint`(…) or a plain call with an extractor argument — the model fills the arguments, then the function runs
context statement a bare template literal on its own line, optionally continued with values (`Page` oncall `when …`) — text every later ask in the scope sees, read at each ask
instruction the backtick text you write: a context statement, an extractor’s prompt, a call hint
prompt what a provider sends; the default rendering is described in The prompt, and receipts carry what was sent
invocation one execution of an infer function — the scope every ask inside it shares
provider the service supplying inference, such as OpenAI; a configured model instance goes in the model entry of nola.config.ts

Next: Intents