# Models and pricing

Use the Vercel or OpenAI adapter for standard and premium calls.

Choose the adapter that matches your existing model code. Both use the same
session, admission checks, and premium settlement lifecycle.

## Standard calls with the Vercel AI SDK

```ts
import { enlitt, ENLITT_STANDARD_MODEL } from "@enlitt/sdk/vercel";
import { generateText, streamText } from "ai";

// Run inside session middleware, or pass { session } to enlitt().
const model = enlitt(ENLITT_STANDARD_MODEL);
const result = await generateText({ model, prompt: "Explain photosynthesis." });
console.log(result.text);

const stream = streamText({ model, prompt: "Explain photosynthesis." });
for await (const text of stream.textStream) {
  process.stdout.write(text);
}
```

The standard model has a **minimal, stable contract**:

* Text input and output, with streaming or non-streaming responses.
* Bounded PNG and JPEG image input.
* Automatic function tools, with calls and results across steps; omitting tools disables tool calling.
* Output-token limits, cancellation, normalized usage, and a finish reason.
* At least 131,072 context tokens and 32,768 output tokens.

The SDK **rejects locally** unsupported standard options: forced tool choice,
structured-output enforcement, sampling and parallel-tool controls, files,
audio/video input, reasoning controls, and provider-specific options.
The standard identifier does not promise every feature of either adapter's underlying SDK.

## Premium calls with the Vercel AI SDK

Wrap a provider model you already configure and pay for. The example accepts
that model so it does not assume a particular provider or model name:

```ts
import { enlitt, type EnlittTarget } from "@enlitt/sdk/vercel";
import { generateText } from "ai";

type ProviderModel = Exclude<EnlittTarget, string>;

export async function summarize(myProviderModel: ProviderModel, prompt: string) {
  return generateText({
    model: enlitt(myProviderModel),
    prompt,
    providerOptions: {
      enlitt: {
        maxCharge: "10000000", // Reserve up to $0.01, subject to the user's limits.
        price: "5000000" // Charge $0.005 on completion or cancellation.
      }
    }
  });
}
```

Enlitt checks the session and spending capacity before invoking your provider.
If admission succeeds, your server calls the provider directly. You still
supply and pay for the provider credentials. A failed provider call charges
the user zero; provider errors propagate to your code unchanged.

### Maximum and actual price

All monetary inputs are **NANOUSD integer strings**: `"1000000000"` is $1,
`"10000000"` is $0.01, and `"0"` is valid. Use `BigInt` for arithmetic;
never send decimals, signs, exponent notation, or leading zeros.

| Options                                                | Behavior                                                                                                                       |
| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| Constant `price` only                                  | The maximum defaults to that price.                                                                                            |
| `maxCharge` and constant `price`                       | Reserves the maximum; charges the constant, capped by the effective maximum. The constant cannot exceed your declared maximum. |
| `maxCharge` and function `price`                       | Runs the function once on completion or cancellation and caps its result.                                                      |
| `maxCharge` without `price`                            | Accounts the call but charges zero.                                                                                            |
| Function `price` without `maxCharge`, or neither field | Rejected before a network request.                                                                                             |

The old `charge` object is unsupported. Use `maxCharge` and `price` instead.
Both are forbidden on standard calls.

For token-based pricing, supply rates in NANOUSD **per token**. These example
rates are illustrative, not a provider price list:

```ts
import { perToken } from "@enlitt/sdk/vercel";

const pricing = {
  maxCharge: "10000000",
  price: perToken({ input: "100", output: "200" })
};
// Pass pricing as providerOptions.enlitt on your premium AI SDK call.
```

`perToken` multiplies total input and output token counts using exact integer
arithmetic. Missing usage counts as zero. Cached and reasoning tokens are
included in the corresponding totals. A custom function receives usage,
output, timing, status, and `effectiveMaxChargeNanoUsd` in your server process.
It never runs for failed calls. If pricing throws or returns an invalid value,
the charge is zero and `onTerminalReportError` receives the error.

## OpenAI Responses adapter

The standard path needs no OpenAI provider client:

```ts
import { enlitt, ENLITT_STANDARD_MODEL } from "@enlitt/sdk/openai";

const client = enlitt();
const response = await client.responses.create({
  model: ENLITT_STANDARD_MODEL,
  input: "Explain photosynthesis."
});
console.log(response.output_text);
```

For premium, wrap your existing official OpenAI client. Set the example
`OPENAI_MODEL` environment variable to a model available to your provider account:

```ts
import OpenAI from "openai";
import { enlitt } from "@enlitt/sdk/openai";

const model = process.env.OPENAI_MODEL;
if (!model) throw new Error("Set OPENAI_MODEL on the server");

// OpenAI reads your server's OPENAI_API_KEY; Enlitt reads ENLITT_SECRET_KEY.
const client = enlitt(new OpenAI());
const response = await client.responses.create({
  model,
  input: "Summarize this support ticket.",
  enlitt: { maxCharge: "10000000", price: "5000000" }
});
console.log(response.output_text);
```

This adapter supports **`responses.create()` only**. It does not expose Chat
Completions, embeddings, images, audio, Realtime, background responses, or
Responses helper methods such as `parse`, `retrieve`, and `stream`.
For streaming, pass `stream: true` to `responses.create()` and consume the
returned async iterable with `for await`. Consume or cancel streams so their
terminal outcome can be reported. `await` and `for await` are supported;
OpenAI's `.withResponse()` and stream helper objects are not.

## Call options and reporting

Both adapters accept construction options for `secretKey`, `baseUrl`,
`session`, `rateLimit`, and `onTerminalReportError`. The key defaults to
`ENLITT_SECRET_KEY`; `baseUrl` defaults to `ENLITT_BASE_URL`, then the deployed
API origin, and must use HTTPS.

Vercel per-call options go in `providerOptions.enlitt`; OpenAI per-call options
go in the request's `enlitt` field. Both support `idempotencyKey` and string
`metadata`. Metadata is limited to 16 entries, 64-character keys, and
512-character values; keys cannot start with `enlitt_`.

Premium calls may set `reportingDeadlineMs` to allow enough time for generation
and pricing. Enlitt bounds that deadline by server policy. A call that expires
before settlement charges zero. Reporting failure is sent to
`onTerminalReportError` and does not replace the provider's result or error.

For a direct SDK call or raw HTTP request outside these adapters, use
[`withPremiumCharge()`](/docs/usage.md). See [Spending limits](/docs/premium-limits.md)
and [Idempotency](/docs/idempotency.md) before adding retries.
