---
title: 'AI assistant'
description: 'The storefront chat assistant, its model, and how it shares the tool registry with MCP.'
canonical: 'https://cartwright.app/docs/features/ai-assistant'
---

# AI assistant (/docs/features/ai-assistant)



Every Cartwright shop has a chat assistant available to customers from a floating button on the storefront. It is wired to `app/api/assistant/chat/route.ts` and powered by Anthropic Claude.

## Model [#model]

The default model is **Claude Haiku 4.5** (`claude-haiku-4-5`), configured in `lib/ai/client.ts`. Haiku is the right pick for storefront chat: cheap, fast, capable enough to orchestrate tool calls and produce honest product recommendations.

Swapping providers or models is a one-file change. The client uses Vercel's AI SDK, so anywhere you can plug `ai-sdk/openai`, `ai-sdk/google`, or another provider, the storefront chat will follow.

<Callout type="tip">
  For heavier reasoning workloads (e.g. admin-side content generation), point a different module at Claude Sonnet or Opus. Haiku stays on the customer chat path where speed matters more than depth.
</Callout>

## Key management [#key-management]

`getAnthropicApiKey()` reads from two places, in order:

1. `IntegrationSettings.id=1` in the database — set via `/admin/integrations`, encrypted with AES-256-GCM.
2. `process.env.ANTHROPIC_API_KEY` as fallback for local dev.

A 30-second in-process cache prevents repeated DB lookups on chat bursts. Admin changes propagate within that window.

If no key is configured anywhere, the chat endpoint returns `503` and the storefront FAB shows a graceful "AI is unavailable right now" state.

## Tool surface [#tool-surface]

The assistant calls into the same `lib/tools/registry.ts` that MCP and the REST endpoint at `/api/v1/tools` use. Storefront chat sessions get a JWT with restricted scopes — typically `catalog:read` and `cart:write` — so even a jailbroken prompt cannot reach admin-CRUD tools.

The tool list the chat sees is filtered by scope at registry-call time. Tools the session does not have scope for are simply not exposed.

## Streaming [#streaming]

Responses stream via Vercel AI SDK's `streamText`. The storefront component reads the stream token-by-token. Tool calls within a stream are surfaced as structured events; the UI can render "Looking up products..." indicators while a tool runs.

## Generative product UI [#generative-product-ui]

The assistant doesn't just return text — it can choose **how** to present products. The whitelisted `ui.present_products` tool lets the model pick one of three layouts — `grid`, `spotlight`, or `comparison` — plus the product slugs; the server then fetches the real data from the database. The model never emits markup, so there's no XSS surface: it selects a layout and products, and a deterministic renderer (`AIStylistPanel`) draws it. The note text renders as React-escaped strings, and the tool is `catalog:read` only.

## Token-level cost metering [#token-level-cost-metering]

Every AI call on the admin and assistant chat routes is metered for token usage (`lib/ai/usage.ts`), so spend is observable per request rather than a monthly surprise on a provider invoice. Combined with the [audit log](/docs/features/audit-log-revert) — which already stamps provider, model, and modality — you can attribute AI cost down to the individual tool call.

## What runs on Gemini instead [#what-runs-on-gemini-instead]

Cartwright also integrates Google Gemini, but **not** for storefront chat. `lib/ai/gemini.ts` is used for:

* Image composition / reference-image-driven generation (`composeWithReferenceImages`).
* Theme color extraction (image → palette suggestions in the admin theme generator).
* Category content generation (`lib/ai/category-seo-generator.ts`).

The clear split: Anthropic for conversational and tool-orchestrating workloads, Gemini for image-aware generation tasks.

## Disabling [#disabling]

Set `brand.config.ts:ai.enabled = false` to ship a shop with no AI surface. The FAB disappears, the route returns 410 Gone, and the bundle drops the chat client component entirely.

<Cards>
  <Card title="Architecture: tool registry" href="/docs/architecture/tool-registry" />

  <Card title="Recipe: AI prompt tuning" href="/docs/recipes/ai-prompt-tuning" />
</Cards>
