Skip to main content

Connect-provider drawer

The connect-provider drawer is the right-side surface that slides over the providers list when a user clicks Connect provider. It replaces the legacy multi-step connect/ wizard with a single in-place form that validates credentials, discovers models, and submits the provider end-to-end. The drawer mounts whenever the providers-list URL carries ?connect=open — the three CTAs on the list page (header button, empty-state primary, “connect another” strip) all flip that query param. PR #1078 shipped the visual layer; PR #1086 wired the real submit, test, and discovery paths described here.
The drawer is the only supported entry point for creating providers from the UI. The previous providers/connect route now 308s to ?connect=open so inbound links from emails and docs continue to work.

Test-strategy buckets

Every catalog entry declares a testStrategy that picks one of five sub-handlers on the API. The dispatch enum lives on ProviderCatalogEntry in apps/web/src/lib/provider-catalog.ts and is mirrored on the API side in apps/api/src/domains/llm-routing/model/provider-catalog.ts. A drift-detector test keeps the two in lockstep.
BucketStrategyProvidersUser-facing behaviour
Alist-models20 — openai, mistral, groq, cerebras, together, deepseek, deepinfra, xai, openrouter, perplexity, nvidia-nim, nebius, novita, anyscale, moonshot, vllm, tgi, lmstudio, litellm, customReal GET {baseUrl}{modelListPath} with Authorization: Bearer. Discovered models hydrate the models section.
Blist-models-quirk7 — anthropic, gemini, cohere, ollama, qwen, huggingface, ai21Same as A but the sub-handler swaps the path or auth header (e.g. anthropic’s x-api-key, gemini’s ?key=, ollama’s /api/tags).
Clist-models-templated6 — cloudflare, fireworks, databricks, ibm-watsonx, snowflake, azure-openaiThe base URL contains <placeholder> segments filled from drawer-collected templateFields before the request fires.
Dcloud-sdk2 — bedrock, vertexIn-drawer Test is disabled with the tooltip “Test connection available in a follow-up — your credentials will be validated on first inference.” Submission still persists the provider.
Eprobe-completion4 — sambanova, voyage, replicate, zaiNo /models endpoint exists, so the handler issues a tiny chat / embedding / account call to confirm the credential works.
Counts sum to 39 — the full catalog. The bucket assignments are the source of truth for the dispatch table at apps/api/src/domains/llm-routing/use-cases/test-strategy.ts.

Adding a new provider

The drawer auto-renders any provider with a complete catalog entry. To add one:
  1. Append the entry to apps/web/src/lib/provider-catalog.ts with id, name, tier, auth, baseUrl, description, logoSlug, and — required — testStrategy. The existing fields are documented inline in ProviderCatalogEntry.
  2. Mirror the entry in apps/api/src/domains/llm-routing/model/provider-catalog.ts. This is the API-side dispatch catalog the test handler reads. Both files must agree on id and testStrategy.
  3. Run the drift-detector tests to confirm the two catalogs agree:
    bun test apps/web/src/lib/provider-catalog.test.ts
    bun test apps/api/src/domains/llm-routing/model/provider-catalog.test.ts
    
  4. If testStrategy is list-models-quirk, add a sub-handler branch in apps/api/src/domains/llm-routing/use-cases/test-strategy.ts. Anchor on an existing quirk (anthropic’s x-api-key is the simplest reference).
  5. If testStrategy is list-models-templated, populate templateFields with the placeholder inputs the drawer must collect (accountId, workspace, resourceName, project, or region). The drawer’s identity section renders one input per field.
  6. If testStrategy is cloud-sdk (bedrock, vertex), no test handler is wired. Submission still works; the in-drawer Test button is disabled with the deferred-validation tooltip. The credential is validated on the first real inference call.
modelListPath defaults to /v1/models. Only override it when the upstream exposes models at a non-standard path (ollama’s /api/tags, groq’s /openai/v1/models).

Audit log

Every test call — both the pre-create route (POST /providers/test-credential) and the existing per-credential route (POST /providers/:id/credentials/:credId/test) — writes a row to provider_test_audit. Columns:
ColumnNotes
iduuid PK
project_idFK to projects, ON DELETE CASCADE
user_idFK to users, ON DELETE CASCADE
provider_idCatalog id; not a FK (the provider row may not exist yet)
credential_idNullable FK to llm_provider_credentials, ON DELETE SET NULL
okBoolean — whether the upstream call succeeded
test_strategyThe bucket letter / strategy enum that ran
upstream_statusNullable HTTP status from the upstream
error_kindauth | network | rate-limit | not-found | upstream | test-deferred | unknown
duration_msWall-clock duration
created_attimestamptz
No secret material — keys, tokens, response bodies — is ever persisted. Retention is 90 days, enforced by a cron job (TODO — confirmed during implementation).

Rate limiting

The pre-create test route is per-user rate-limited: 10 calls / minute / user and 60 calls / hour / user. Bursts return 429 with a Retry-After header, which the drawer surfaces as error: "rate-limit" (“Provider rate limit hit, try again”). The current implementation uses an in-memory sliding window; a Redis-backed counter is on the follow-up list so the limit holds across multi-instance API rollouts.

OAuth — deferred

OAuth is in the catalog (auth: ["api-key", "oauth"]) for the providers that support it, but the drawer hides the OAuth option from the auth segmented control until the redirect-callback flow lands. Providers that will gain an OAuth tab in the follow-up:
  • Google Gemini (gemini)
  • Azure OpenAI (azure-openai)
  • Google Vertex AI (vertex) — also supports service-account
  • Databricks (databricks)
  • Snowflake Cortex (snowflake)
  • IBM watsonx (ibm-watsonx)
Until then the user picks API-key (or the cloud-credential variants for vertex / bedrock) and the OAuth option is simply not rendered. Catalog values do not change — only the drawer’s display filter does.

See also

Providers

What a provider is and how credential resolution works at the gateway layer.

Supported providers

Per-provider format, streaming, and tool-call matrix.
Internal references:
  • Visual-layer plan — specs/2026-05-10-create-provider-drawer-plan.md
  • Wiring plan — specs/2026-05-11-connect-drawer-wiring-plan.md