AI assistant
The storefront chat assistant, its model, and how it shares the tool registry with MCP.
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
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.
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.
Key management
getAnthropicApiKey() reads from two places, in order:
IntegrationSettings.id=1in the database — set via/admin/integrations, encrypted with AES-256-GCM.process.env.ANTHROPIC_API_KEYas 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
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
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
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
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 — which already stamps provider, model, and modality — you can attribute AI cost down to the individual tool call.
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
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.
Storefront UX toggles
Small storefront UX flags — the first-visit welcome guide and the promo announcement bar — both runtime, default-off, ecommerce-only.
Local AI / Ollama
Toggle between cloud Anthropic and a local Ollama endpoint with one field. Same Guardian middleware, same audit log, same tool registry.