Usage & BYOK

Every demo feature uses this pattern to communicate key status and quota to the user.

Key status

Shows which key is active on a feature page — the user's own key, or the demo quota with a usage meter.

Own key active — quota notice hidden.

Using your own Rime key — manage in Account.

Your provider keys

Shown on the account page when a user follows the add-key nudge. Supports save, test, and remove; the purpose annotation links the provider to its feature.

No key saved.

Rime

Used for Text-to-Speech

Get a key

Demo quota

The quota system tracks usage across all features. The meter appears inline on each feature page; the summary card appears on the account page for a full overview.

75% consumed.

You have used 75% of your Text-to-Speech demo allowance. View settings

Text-to-Speech
75%
Speech-to-Text
53%

Features

  • Key resolution via resolveProviderKey. Returns { key, source: 'byok' | 'app' }. BYOK takes precedence over the app env var.
  • Quota only applies to source === 'app'. Users with BYOK bypass the demo limit entirely.
  • Account page shows demo usage for features without BYOK. Built from usageFeatures config — absence of a DB row shows 0%, presence of BYOK hides the feature from the section entirely.
  • Chat uses one shared chat quota spanning all providers. A user with BYOK for all four chat providers has no demo quota tracked.

Setup

Add the provider's API key to .dev.vars and .env for local dev:

# Single-provider features
RIME_API_KEY=
ASSEMBLYAI_API_KEY=

# Chat providers (all optional; each one independently enables that provider in the demo)
ANTHROPIC_API_KEY=
GEMINI_API_KEY=
OPENAI_API_KEY=
OPENROUTER_API_KEY=

For production, set as Cloudflare Worker secrets via wrangler secret put <NAME>.

Implementation details

  • Key resolver — src/lib/server/svelm/key-resolver.ts. ENV_VAR_MAP maps each Provider to its env var. resolveProviderKey(provider, locals, platform) checks BYOK first, then falls back to the env var.
  • Usage config — src/lib/configs/usage.config.ts. Each entry has featureKey, displayName, unitLabel, defaultLimit, and optionally provider (omitted for multi-provider features like chat).
  • Feature endpoint pattern — call resolveProviderKey, gate consumeTokens on resolved.source === 'app'.
  • Account page — account/+server.ts iterates usageFeatures, skips features where BYOK covers the provider, and returns DB row data (or defaults) for the rest. Returns null when all features are covered.
  • Chat topology — chat-handler.ts accumulates totalTokens across the agentic loop and calls consumeTokens(db, userId, 'chat', totalTokens) after the stream completes. The single chat feature key is shared across all four chat providers.
  • STT usage endpoint — src/routes/api/(svelm)/stt/usage/+server.ts. Accepts POST { seconds }, silently no-ops for unauthenticated or BYOK users.
  • DictateButton — src/lib/svelm/stt/dictate-button.svelte. Records duration and POSTs to /api/stt/usage on stop.

Applying to a new single-provider feature

  1. Add the provider to Provider in src/lib/svelm/agents/types.ts and PROVIDERS in agents/keys.ts.
  2. Add provider: 'ENV_VAR_NAME' to ENV_VAR_MAP in key-resolver.ts.
  3. Add a usageFeatures entry in usage.config.ts with a provider field.
  4. In the feature's API endpoint, call resolveProviderKey(provider, …) and gate consumeTokens on source === 'app'.
  5. Add the provider to the providers array in ai-provider-keys.svelte (account UI).
  6. Add the env var to .env.example, .dev.vars.example, and DEV_VARS_KEYS in scaffold.js.

The account page demo usage section updates automatically — no changes needed there.

AboutDocDemo