pleach
Get Started

Provider detection

How `@pleach/core/quickstart` resolves an env var to a provider — priority order, overrides, custom aliases.

The createPleachRoute() handler from @pleach/core/quickstart auto-detects which provider to use based on which env var you set. This page documents the resolution matrix, the locked default priority, and how to override it.

The resolution matrix

The four cases the regression-lock test at packages/core/test/quickstart/providerDetection.test.mjs enforces:

OPENROUTER_API_KEYANTHROPIC_API_KEYdetectProvider() returns
setunset"openrouter"
unsetset"anthropic"
setset"anthropic" (alphabetical)
unsetunsetnull → route returns HTTP 503

The third row is the locked behavior consumers most often ask about. Default priority is alphabetical, not preference-biased. Anthropic precedes OpenRouter alphabetically, so it wins.

This is intentional. The alphabetical default avoids encoding any OpenRouter-primary preference into the "I just installed Anthropic SDK and it works" experience. Operators that want a specific priority pass it explicitly.

Full default priority

anthropic → deepseek → google → mistral → moonshot → openai → openrouter

These map to six of the seven ProviderFamily values exported from @pleach/core/modelfamily (xai has no standalone env-var detection) plus openrouter as a gateway transport. The resolution matrix itself is host-supplied — reached through the AgentAdapter.resolveModel<C>() seam, not shipped in the package.

Default env-var aliases per provider (from DEFAULT_PROVIDER_ENV_VARS):

ProviderEnv var(s)
anthropicANTHROPIC_API_KEY
deepseekDEEPSEEK_API_KEY
googleGOOGLE_GENERATIVE_AI_API_KEY, GOOGLE_API_KEY
mistralMISTRAL_API_KEY
moonshotMOONSHOT_API_KEY
openaiOPENAI_API_KEY
openrouterOPENROUTER_API_KEY

Google ships keys under two names — the newer GOOGLE_GENERATIVE_AI_API_KEY (used by @google/generative-ai) and the legacy GOOGLE_API_KEY. The newer name is checked first.

Custom priority

Pass providers to override:

import { detectProvider } from "@pleach/core/quickstart";

// OpenRouter wins when both keys are set.
const provider = detectProvider({
  providers: ["openrouter", "anthropic", "openai"],
});

Pass a subset to restrict detection — providers not in the list are ignored even if their env vars are set:

// Only consider Anthropic and OpenAI. OpenRouter is ignored.
const provider = detectProvider({
  providers: ["anthropic", "openai"],
});

Custom env-var aliases

Override per-provider env-var names via envVars:

const provider = detectProvider({
  envVars: {
    anthropic: ["MY_CORP_ANTHROPIC_KEY", "ANTHROPIC_API_KEY"],
  },
});

The override replaces the provider's entire alias list. Providers not in envVars inherit defaults.

Test-friendly: supply a synthetic env

By default detectProvider reads from process.env. Pass env to supply your own map — useful in tests, edge environments, and custom config sources:

const provider = detectProvider({
  env: { ANTHROPIC_API_KEY: "sk-ant-test" },
});
// → "anthropic"

Pass env: {} to force a "no provider detected" path without mutating process.env.

Listing all detected providers

detectAvailableProviders() returns every provider with a non-empty env-var value, in priority order. Useful for diagnostics and for createPleachRoute callers that want to enumerate fallbacks:

import { detectAvailableProviders } from "@pleach/core/quickstart";

const available = detectAvailableProviders();
// e.g. ["anthropic", "openrouter"] when both keys are set

Pinning a provider explicitly

If you want to skip detection entirely, pass provider to createPleachRoute:

export const POST = createPleachRoute({
  provider: "anthropic",
});

Detection is skipped; the route uses Anthropic regardless of which other env vars are set.

Honest scope-limit

Provider detection works for the seven default providers above. Custom providers (your own gateway, a self-hosted LLM, a forked SDK) require orchestratorConfig on createPleachRoute or direct SessionRuntime construction — see Upgrading to @pleach/core.

The detection helpers are pure functions over an env map; they do NOT validate that the key works against the provider. A wrong-key failure surfaces later as a provider exception inside the stream, which ChatStreamError.cause preserves.

Where to go next

On this page