Skip to content
Docs

Command palette

Jump to a page or search the docs.

Changelog

AI foundation

How every AI feature gets its model from one registry on TanStack AI, its flag, the plan quota it counts against, and the scrubber on everything it sends.

Markdown

What it does

Every AI feature (the assistant, receipt and voice structuring, the Studio, insights, transcription, embeddings) resolves its model from one registry, packages/api/src/features/ai/ai.models.ts, and calls it through TanStack AI. Each feature has a flag, off by default, and the calls a User makes are counted against the plan's monthly quota. The decision record is ADR-0017; the product rules are in docs/specs/ai-features.md.

Configure a model

VariableValue
AI_PROVIDER, AI_MODELthe text model every text feature uses unless it has its own (ADR-0007)
AI_MODELSJSON, feature to provider:model, e.g. {"studio":"gemini:gemini-3-pro-image-preview"}
ANTHROPIC_API_KEY / OPENAI_API_KEY / GEMINI_API_KEYthe key of each provider in use

Image, transcription and embedding features have no default model. A provider without an adapter for the kind (Anthropic for images) or malformed JSON fails loudly on first use. Then turn the feature's flag on at /admin/flags: ai.assistant, ai.capture, ai.studio, ai.genui, ai.insights, ai.extras.

Quotas

QuotaFreePlus
structuring: scanner, voice, listing and translation by a model20300
studio: Studio images360
copilot: assistant questions20500

Months are calendar months in UTC. A call counts before the model runs, under a per-User lock, so the limit is exact. Past it the procedure answers QUOTA (HTTP 402). The on-device scanner never counts.

Adding an AI feature

  1. A row in AI_FEATURES (kind, flag, quota) and, if new, its flag in packages/flags/src/registry.ts.
  2. In the service: admitAi(db, systemDb, userId, plan, feature, platform) returns the model or fails with AiUnavailable, AiRateLimited or AiQuotaExceeded; then generateStructured, generateImages, transcribe or embedTexts with it. Map failures with aiRpcError.
  3. Tests use fakeText from @tanstack/ai/testing and fakeImage, fakeTranscription, fakeEmbedding from packages/api/src/testing/ai-adapters.ts; no test calls a live model.

Data and privacy

ai_usage keeps one row per counted call: the User, the quota and the time, never a prompt, an answer or an amount. It belongs to the User across households, tenant traffic only reads and adds its own rows, and deleting the User deletes them (ADR-0009). Every chat call ends with the scrubOutbound middleware, which removes plate, VIN and email shapes from the provider's copy of the conversation; image prompts and embedding inputs are scrubbed too.

Procedures

ProcedureWhoReturns
ai.usageany signed-in Userthe plan and this month's use of each quota whose feature is on
Edit on GitHubDocumentation is in English.

We use essential cookies to make our app work. With your consent, we also use optional cookies to improve your experience. Read our Cookie Policy and Privacy Policy.