---
title: "Call a model"
description: "Call a model through ai with limits and audit, and get typed output."
section: "guides"
status: "Building for 0.3"
spec: "ai"
canonical: "https://mainsdk.dev/docs/guides/call-a-model"
updated: "2026-10-28"
---

# Call a model

> Call a model through ai with limits and audit, and get typed output.

Status: Building for 0.3 · Canonical: https://mainsdk.dev/docs/guides/call-a-model · Updated: 2026-10-28

You will configure a driver, call a model, and get output checked against a schema. It takes about 15 minutes.

## You need

- A provider key in your secrets manager (for example `secret:ANTHROPIC_API_KEY`).

For a first run with no key, use `driver: local`: it needs no account and no network and prices every call at `$0`.

## 1. Configure

```yaml title="main.yaml"
ai:
  driver: anthropic
  models: [anthropic/claude-*]
  limits: { per-user: $1/day, total: $500/month }
  prices:
    anthropic/claude-sonnet-4-5: { input: $3, output: $15 }   # a model with no price cannot be called
```

## 2. Generate text

```ts title="app/api/summary/route.ts"
import { ai } from "mainsdk";

const user = { id: "u_1" };
const order = { id: "o_1" };   // your own order record

const text = await ai.generate({
  model: "anthropic/claude-sonnet-4-5",
  prompt: `Summarise order ${order.id}`,
  feature: "order-summary",
  user,
});
```

`feature` and `user` are what spend limits and the audit log group by. The audit record holds the model, tokens and cost, never the prompt.

## 3. Get typed data

```ts title="lib/extract.ts"
import { ai } from "mainsdk";
import { s } from "mainsdk/schema";

const user = { id: "u_1" };
const emailText = "Invoice 42, total $120, due 1 Dec";   // the email body
const Invoice = s.object({ total: s.string(), due: s.string() });
const found = await ai.extract({
  input: emailText,
  schema: Invoice,
  model: "anthropic/claude-sonnet-4-5",
  feature: "invoice-extract",
  user,
});
console.log(found.value.total);
```

Invalid output is repaired once (up to twice with `repairs`), then refused with `MAIN-AI-0011`. It is never returned.

## Check it worked

A call over the per-user limit fails with `MAIN-AI-0007` before anything reaches the provider. With `driver: local` and `per-user: $0/day` you can see it with no account.

## Fail a change that makes a feature worse

Put an eval set next to the feature, then run it before every merge:

```bash
npx mainsdk eval summarise --json
npx mainsdk eval summarise --update-baseline
```

`mainsdk eval` exits 1 when a score drops below its threshold or below the saved baseline. `--update-baseline` writes the baseline only when the run passes, and `--max-cost` stops the run before it spends more than you allow. `mainsdk eval samples summarise --export samples.json` exports the recorded samples for review.

## Next

- [AI calls](https://mainsdk.dev/docs/interfaces/ai)
- [Extract structured data](https://mainsdk.dev/docs/guides/extract-structured-data)
