# AI provider

> The logic layer behind the AI components, with no UI: a typed provider adapter with an offline mock, a credit ledger that keeps subscription and bought credits apart, and a generation queue that charges, refunds and retries.

- Type: Component (data)
- Access: Free, open source (MIT)
- Page: https://goodui.dev/native/components/ai-provider
- Markdown: https://goodui.dev/native/components/ai-provider.md
- Registry item: https://goodui.dev/native/r/ai-provider.json
- Dependencies: none
- Keywords: react native ai hook, generation queue, credits hook, mock ai provider, ai adapter, streaming text provider

## When to use

- Building an AI screen before the backend exists: the mock runs with no keys and no network.
- Running long jobs one or a few at a time, with progress, cancel and retry.

## When not to use

- As your source of truth for credits: useCredits is a display cache, and the server has to enforce the balance.
- Calling a model provider directly from the app: keys in an app can be extracted, so call your own backend.

## Installation

With the shadcn CLI (needs a components.json in the Expo app), then `npx expo install --fix` so native dependencies match your Expo SDK. The foundation comes along the first time.

```bash
npx shadcn@latest add https://goodui.dev/native/r/ai-provider.json
npx expo install --fix
```

The component lands in `components/goodui-native/ai-provider/` and imports the foundation from `components/goodui-native/foundation/` with relative paths.

## Usage

```tsx
import { Button } from "@/components/goodui-native/button/button";
import { createMockProvider, useCredits, useGenerationQueue } from "@/components/goodui-native/ai-provider/ai-provider";
import { GenerationQueue } from "@/components/goodui-native/generation-queue/generation-queue";

const provider = createMockProvider<{ url: string }>({
  durationMs: 4000,
  stages: ["Reading your photo", "Painting", "Finishing"],
  output: () => ({ url: "https://example.com/result.jpg" }),
});

export function Studio() {
  const credits = useCredits({ initial: 12 });
  const queue = useGenerationQueue({ provider, credits });

  return (
    <>
      <Button
        disabled={!credits.canAfford(4)}
        onPress={() => queue.enqueue("Portrait", { prompt: "oil painting" }, 4)}
      >
        Generate
      </Button>
      <GenerationQueue jobs={queue.jobs} onCancel={queue.cancel} onRetry={queue.retry} />
    </>
  );
}
```

## API reference

### createMockProvider

`import { createMockProvider } from "@/components/goodui-native/ai-provider/ai-provider";`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `output` | `(request: AiRequest<Input>) => Output` | – | Result for a request. Runs after the fake wait. |
| `reply` | `(request: AiRequest<Input>) => string` | – | Text for `stream`. |
| `durationMs` | `number` | – | How long one generation takes. |
| `stages` | `string[]` | – | Stage names reported along the way, evenly spaced. |
| `failRate` | `number` | – | Fail this share of generations (0 to 1), to exercise the error states. |
| `tokenMs` | `number` | – | Delay between streamed words. |

### useGenerationQueue

`import { useGenerationQueue } from "@/components/goodui-native/ai-provider/ai-provider";`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `provider` (required) | `AiProvider<Output, Input>` | – | – |
| `concurrency` | `number` | `1` | Jobs that run at once. |
| `onDone` | `(job: GenerationJob<Output, Input>) => void` | – | A job finished. Good moment for a notification. |
| `credits` | `Pick<Credits, "spend" \| "refund">` | – | Pass useCredits' result and the queue does the money: enqueue and retry charge the job's cost, and a failed or cancelled job is refunded once, to the balances it came from. Without it, `cost` is only a number on the job. |
| `onRefund` | `(job: GenerationJob<Output, Input>) => void` | – | A job failed or was cancelled. With `credits` the refund is already done. |
| `onShort` | `(job: GenerationJob<Output, Input>) => void` | – | Retry could not be paid for. Send the person to the store: a silent failed tap looks like a broken button. |

### useCredits

`import { useCredits } from "@/components/goodui-native/ai-provider/ai-provider";`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `initial` (required) | `number` | – | Starting subscription credits. |
| `purchased` | `number` | `0` | Starting bought credits, from your server. |
| `lowAt` | `number` | `10` | The balance the app calls low. The meter warns at or below it. |

## Accessibility

- No UI of its own. GenerationQueue, CreditsMeter and StreamText announce what this layer reports.

## Motion

- None. The mock eases its progress out so progress bars look like a real render.

## Performance

- Each progress report re-renders the hook's owner, so report a few times a second at most.
- Keep useGenerationQueue above your navigator if jobs must survive leaving the screen. Unmounting it aborts running jobs and refunds running and queued ones.

## Notes for AI

- Replace the mock with one object that implements generate and stream and calls your backend. Do not change the screens.
- Reject with AiAbortError when the signal aborts; any other rejection marks the job failed and calls onRefund.
- Pass `credits` to useGenerationQueue and it does the money: enqueue and retry charge, and a failed or cancelled job is refunded once to the balance it came from. Both return null or false when the balance is short.
- Keep the two balances apart: renew(allowance) resets subscription credits each period, and addPurchased adds bought credits, which never expire (App Store rule 3.1.1). spend uses subscription credits first.
- Stage names are shown as they are, so pass translated text or map keys to text in the UI.

## Related

- [Generation queue](https://goodui.dev/native/components/generation-queue.md): Cards for long AI jobs: each one shows its status, a fill and a light sweep while running, a rolling percentage, and cancel, retry and open actions.
- [Credits meter](https://goodui.dev/native/components/credits-meter.md): A balance pill for an app bar whose digits roll when the balance changes and which turns warning colored when low, plus CreditsCost, a live price line that says what a job costs and what is left after it.
- [Stream text](https://goodui.dev/native/components/stream-text.md): Text that arrives word by word: new words fade up and a caret pulses at the end, and when streaming stops it becomes one plain text block.
- [Prompt composer](https://goodui.dev/native/components/prompt-composer.md): The input for AI apps: a field that grows with the text on a spring, chips that add phrases to the prompt, and a send button that morphs into a stop square while a job runs.

## Guidance for AI tools

AI provider: The logic layer behind the AI components, with no UI: a typed provider adapter with an offline mock, a credit ledger that keeps subscription and bought credits apart, and a generation queue that charges, refunds and retries. Use only the documented props. Keep accessibility, reduced motion and both color schemes intact when adapting it.

Full library index: https://goodui.dev/native/llms.txt
