runlot
DataAI

AI

Call language models from your worker. One binding, env.ai, reaches both frontier and value models, and the cost comes out of your org's credit balance.

runlot.json
{ "ai": true }
runlot deploy

After deploy, env.ai is available in your worker. There is no create command — the declaration is the grant.

env.ai only exists inside the worker. A project that ships static assets with no worker entry point cannot use it.

Calling it

const r = await env.ai.run("anthropic/claude-sonnet-5", {
  messages: [{ role: "user", content: "Write the order confirmation email." }],
  maxTokens: 1024,
});

r.text        // the generated text
r.usage       // { inputTokens, outputTokens, cachedInputTokens }
r.model       // the model that actually ran
r.stopReason  // why generation stopped

For a single message, prompt is the shorthand — it is the same as one user message.

const r = await env.ai.run("@runlot/nova-micro", { prompt: "Summarise this in one line." });
Option
messagesArray of { role, content }
promptShorthand for a single user message
systemSystem prompt
maxTokensMaximum tokens to generate
temperatureSampling temperature
streamtrue returns a ReadableStream

role is user or assistant, nothing else. The system prompt goes in system, not in messages.

Streaming

With stream: true you get a ReadableStream of SSE frames. Hand it straight to a Response.

const stream = await env.ai.run("@runlot/nova-micro", { prompt, stream: true });
return new Response(stream, { headers: { "content-type": "text/event-stream" } });

The frames look like this.

data: {"delta":"Hello"}

data: {"delta":" there"}

event: done
data: {"model":"@runlot/nova-micro"}

If generation is cut short, the last frame is event: error.

event: error
data: {"message":"…"}

Once the stream has started, a failure arrives as that frame, not as a thrown error — the 200 went out with the first frame. Handle event: error wherever you read the stream.

The catalog

Read the list of models this project can actually call from your code.

const models = await env.ai.models();

There are two lanes, sold differently.

LaneModel IDPrice
Frontieranthropic/… · openai/… · google/…The provider's list price, passed through with no markup
Value@runlot/…Set by runlot

A model ID that is not in the catalog is a 400. The list is a fixed one we curate.

The free allowance

Every org gets $0.11 a day to spend on value models. It resets at UTC midnight. With no credit at all, you can still call @runlot/… models up to that much every day.

Frontier models get no free allowance. Open the daily amount up to frontier models and a single call consumes all of it — that is not a free tier, it is a broken one. anthropic/…, openai/… and google/… need credit on the org; without it you get a 402.

Credit is money

Credit is a money balance, not a bundle of tokens. What a call deducts is whatever the price table says at the time of the call.

Provider list prices move — Google's, for one, doubles on 2027-01-01. Sell credit as a bundle of tokens and that day silently reprices what someone already bought. A money balance is what prevents that: you bought an amount, and what it buys is decided when you call.

Both lanes are priced in the same currency. The only difference is where the price comes from. There is no separate unit to learn.

Screens and the CLI show USD. The integers in the API and in --json output are micro-cents (1 micro-cent = 1e-8 USD). In cents, a small call would round to zero and be silently free — a thousand-token call is less than a hundredth of a cent.

Limits

FreePro
Concurrent calls (per project)210
Calls per minute (per project)30300
maxTokens per call4,096Model maximum
Input per call128k tokensModel maximum
Free daily allowance (value)$0.11 per orgSame

Errors

A refused or failed call throws. The error carries two things — e.status (the HTTP status) and e.code (a short string).

StatuscodeMeaning
400unknown_modelThe model ID is not in the catalog. env.ai.models() is the list.
402no_creditNo credit, or the free daily allowance is used up.
413input_too_largeThe input is over the per-call cap.
429rate_limitedThe per-minute limit.
429too_many_concurrentThe concurrency limit.
429upstream_rate_limitedThe provider is busy.
502upstream_errorThe provider call failed.
503model_unavailableThat model cannot be called on this deployment.
504upstream_timeoutThe provider call ran out of time.

rate_limited and upstream_rate_limited come with a Retry-After response header. too_many_concurrent does not — we cannot say when a slot frees. It clears when a call in flight finishes.

Topping up

Top up from the Billing tab on your org screen in the dashboard.

Auto top-up

When the balance falls below a level you set, the saved card is charged automatically. It is off by default.

Top-up amountOne of $10 · $20 · $50 · $100
ThresholdGreater than 0, and less than the top-up amount
Charges per day5
Consecutive failures3 turns it off
  • The first top-up is manual. That payment is where the card gets saved. With no saved payment method, auto top-up cannot be turned on.
  • Turning it on is explicit consent. The card is charged while you are not at a payment screen, so the screen states the charge conditions in full and asks you to confirm them.
  • Five charges a day is the spend ceiling. The daily maximum is 5 × the top-up amount. Buggy code calling in a loop stops there. The count is not adjustable — raise the top-up amount instead.
  • The threshold has to be above 0. Topping up is asynchronous, so there has to be enough balance left to carry you until it lands. A threshold of 0 is refused.
  • The threshold has to be below the top-up amount. Otherwise one top-up still leaves you under the threshold, and it keeps topping up to the daily cap. That is refused too.
  • Three consecutive declines turn it off. Each failure sends a notification, and the third one turns the setting off. We do not retry a customer's card quietly and forever.

CLI

runlot ai                 # calls, tokens, money spent, and your limits
runlot ai models          # the catalog with per-1M-token prices
runlot ai credit          # the org's credit balance and auto top-up settings
runlot ai delete          # turn it off (admin)

All three read commands take --json. There is no command that calls a model — the CLI is outside the worker, and env.ai is reachable only inside it.

What is not here

  • Arbitrary model pass-through. The catalog is a fixed list we choose and maintain.
  • A way in from outside. env.ai is reachable only from inside your deployed worker. There is no API key, no external endpoint, and no way to call it from outside runlot. This is a boundary we drew, not a feature we forgot.
  • Bring your own key. There is no path that takes your provider key and calls through us.
  • Embeddings and vector search, fine-tuning, image and audio models. Not yet.

On this page