Models and providers
A provider is a vendor, such as openai, anthropic or google. A model is the configured instance returned by its factory, such as openai("gpt-5-mini").
Set model to a single instance or a named map. A single instance is shorthand for { default: … }. In a map, default is required and handles a plain ask. Choose names for the other entries and use them with ask with <name> or .withModel("<name>").
The simplest config lets the Nola platform serve inference:
import { defineConfig } from "@nola-lang/runtime";
export default defineConfig({ model: "nola" }); // reads NOLA_API_KEY; the platform picks the modelThe string "nola" is shorthand for nola.infer(). It is the only string accepted directly as a model.
When the default model is "nola", ask with <name> first looks for a matching model-map entry. If there is none, Nola sends the name as an inference profile for the platform to resolve into a model and parameters. This lets you use names such as fast or careful without choosing the underlying model in your code. To choose it explicitly, use nola.infer("<provider>/<model>").
With a local default model, every name must match an entry in the map. An unknown name produces NOLA3004.
import { anthropic, openai } from "@nola-lang/providers";import { defineConfig } from "@nola-lang/runtime";
export default defineConfig({ model: { default: openai({ model: "gpt-5-mini" }), fast: openai({ model: "gpt-5-nano" }), careful: anthropic({ model: "claude-sonnet-4-5" }), },});export infer function summarize(.text: string) { const draft = ask with fast `a rough summary`: string; return ask with careful `a polished summary of: ${draft}`: string;}In this example, fast and careful select the corresponding entries. An unknown name produces NOLA3004, with a list of configured names.
Factories
Section titled “Factories”Import openai, anthropic and google from @nola-lang/providers. Pass a model string when you only need to choose a model: openai("gpt-5-mini") is equivalent to openai({ model: "gpt-5-mini" }). Use the object form for additional options.
The platform factories, nola.infer() and nola.tracer(), come from @nola-lang/runtime. See Providers API.
| Option | openai |
anthropic |
google |
|---|---|---|---|
model (required) |
e.g. "gpt-5-mini" |
e.g. "claude-sonnet-4-5" |
e.g. "gemini-2.5-flash" |
apiKeyEnv (default) |
OPENAI_API_KEY |
ANTHROPIC_API_KEY |
GEMINI_API_KEY |
apiKey |
inline key (prefer the env var) | same | same |
baseUrl |
any Chat-Completions-compatible endpoint | Anthropic-compatible endpoint | Gemini-compatible endpoint |
fetch |
an injectable fetch implementation |
same | same |
maxOutputTokens |
— | provider-level default cap | — |
Factories read API keys at the first request, so the config can load without a key. A missing key produces a provider error at the first ask. The platform model reads NOLA_API_KEY in the same way. It lets the hosted service choose the model; trial keys are limited to the models allowed by the service. Set NOLA_API_URL to use a different platform endpoint during development.
OpenAI-compatible endpoints
Section titled “OpenAI-compatible endpoints”Use openai({ baseUrl }) for services that support the Chat Completions API:
import { openai } from "@nola-lang/providers";import { defineConfig } from "@nola-lang/runtime";
export default defineConfig({ model: { // Ollama (local; the key is required by the dialect but ignored by the server) default: openai({ baseUrl: "http://localhost:11434/v1", apiKey: "ollama", model: "llama3.2" }), // OpenRouter (one key, every model) router: openai({ baseUrl: "https://openrouter.ai/api/v1", apiKeyEnv: "OPENROUTER_API_KEY", model: "deepseek/deepseek-chat" }), // Groq groq: openai({ baseUrl: "https://api.groq.com/openai/v1", apiKeyEnv: "GROQ_API_KEY", model: "llama-3.3-70b-versatile" }), // DeepSeek deepseek: openai({ baseUrl: "https://api.deepseek.com/v1", apiKeyEnv: "DEEPSEEK_API_KEY", model: "deepseek-chat" }), // xAI xai: openai({ baseUrl: "https://api.x.ai/v1", apiKeyEnv: "XAI_API_KEY", model: "grok-4" }), },});Some backends generate an answer before checking its schema. For Groq-style json_validate_failed errors, openai() recovers that answer so Nola can validate it. Nola always checks the returned JSON against the requested schema, even if the backend has already checked it.
mockProvider
Section titled “mockProvider”Use mockProvider for examples and tests that need no API key or network access. Pass a queue of values, consumed one per provider call, or a function that receives the request. A correction attempt consumes another queue entry:
import { mockProvider } from "@nola-lang/providers";import { defineConfig } from "@nola-lang/runtime";
export default defineConfig({ model: { default: mockProvider([{ name: "Alice Smith", age: 32 }]), echo: mockProvider((req) => ({ prompt: req.prompt.messages[0]?.content })), },});See Testing for examples.
Routing precedence
Section titled “Routing precedence”Nola chooses a model in this order:
forceModeloverrides every ask, including those that useask withor.withModel(). It must name an entry in the model map; otherwise Nola raisesNOLA3004.ask with <name>or.withModel(…)selects a model for that ask. If the default model is the platform and the name has no matching map entry, the name becomes an inference profile and the request goes to the default model.model.defaulthandles asks with no explicit selection. A singlemodelvalue becomes the default automatically.
An inference profile is still sent when forceModel is set, although a mock provider ignores it. The profile is part of the ask fingerprint, so ask with fast and a plain ask have separate replay entries.
For CI, use forceModel to send every ask to a mock:
import { mockProvider, openai } from "@nola-lang/providers";import { defineConfig } from "@nola-lang/runtime";
export default defineConfig({ model: { default: openai({ model: "gpt-5-mini" }), mock: mockProvider(() => ({ ok: true })), }, // hermetic — EVERY ask goes here, even .withModel()-pinned ones forceModel: process.env.CI ? "mock" : undefined,});Because forceModel also overrides model choices inside dependencies, those asks use the mock during a test run.
Writing your own provider
Section titled “Writing your own provider”To implement a provider, return an object with a name and an infer(request) method that resolves to { text }; render the request’s intent yourself or with renderPrompt. See the contracts and example in Providers API.
Next: Resilience