Back to Cherry Studio

Provider Resolution

docs/references/ai/provider-resolution.md

2.0.06.6 KB
Original Source

Provider Resolution

The problem this solves

A request needs to know which @ai-sdk/* package to import, with which settings, hitting which URL. Three pieces of state determine that:

FieldLives onExample
provider.idProvider rowminimax, silicon, my-relay
endpointTypemodel.endpointTypes[0] or provider.defaultChatEndpointopenai-chat-completions, anthropic-messages
adapterFamilyprovider.endpointConfigs[endpointType].adapterFamilyopenai-compatible, anthropic, azure-responses

adapterFamily is the actual SDK selector. provider.id is the user-facing identity. endpointType is the protocol family. The mapping is written once at provider-creation time; runtime resolution is read-only.

See Adapter Family for the full design.

Resolver

src/main/ai/provider/endpoint.ts exposes four pure helpers:

ts
resolveEffectiveEndpoint(provider, model): { endpointType, baseUrl, providerOptionsKey? }
resolveProviderVariant(baseProviderId, endpointType): AppProviderId
resolveAiSdkProviderId(provider, endpointType): AppProviderId
resolveProviderOptionsKey(aiSdkProviderId, context): string

resolveAiSdkProviderId is the runtime hot-path entry. It reads provider.endpointConfigs[endpointType].adapterFamily, applies the variant suffix if the endpoint type has one, falls back to openai-compatible when no family is set.

resolveEffectiveEndpoint also resolves registered multi-backend gateway routes (AiHubMix and DMXAPI) once. The route supplies both the wire endpoint and the providerOptions namespace used by that gateway's concrete model. The result is carried through SdkConfig; reasoning encoders consume the resolved namespace and never inspect provider or model ids themselves.

ts
// Full resolver — 6 lines
export function resolveAiSdkProviderId(provider, endpointType) {
  const adapterFamily = endpointType
    ? provider.endpointConfigs?.[endpointType]?.adapterFamily
    : undefined
  if (adapterFamily && adapterFamily in appProviderIds) {
    return resolveProviderVariant(appProviderIds[adapterFamily], endpointType)
  }
  return appProviderIds['openai-compatible']
}

Variants

Some bases expose variant ids (a different endpoint on the same base). resolveProviderVariant knows two suffix rules and applies one only when the resulting <base>-<suffix> id is actually registered — otherwise it returns the base unchanged:

Endpoint typeSuffix tried
openai-chat-completions, ollama-chat-chat
openai-responses-responses

Variants registered today (declared in each provider extension's variants array, packages/aiCore/src/core/providers/core/initialization.ts):

BaseVariant id(s)
openaiopenai-chat (the base openai is itself the Responses API)
azureazure-responses, azure-anthropic
xaixai-responses
cherryincherryin-chat

ollama has no registered variant, so an ollama-chat endpoint resolves to the base ollama. Likewise there is no openai-responses variant (the base already is). azure-anthropic is not reached through the suffix rule — it is selected inside buildAzureConfig when the model is a Claude model (see below). resolveProviderVariant(baseId, endpointType) is idempotent when the base id is already a variant.

Provider config

providerToAiSdkConfig(provider, model, { resolvedEndpoint? }) (src/main/ai/provider/config.ts) returns { providerId: AppProviderId, providerSettings: AppProviderSettingsMap[id] }. The standard request path passes its already-resolved endpoint into this function, which then calls resolveAiSdkProviderId and dispatches through an ordered { match, build } table to build the provider-specific settings object (apiKey, baseURL, organization, headers, ...). Direct callers may omit the option and let the function resolve the endpoint itself.

The builder table (config.ts, first match wins):

MatchBuilderNotes
id === copilotbuildCopilotConfigasync — fetches a Copilot token
id === 'cherryai'buildCherryAIConfig
isOllamaProviderbuildOllamaConfig
isAzureOpenAIProviderbuildAzureConfigreturns azure / azure-responses / azure-anthropic (Claude on Azure)
id === 'bedrock'buildBedrockConfig
id === 'google-vertex'buildVertexConfigreturns google-vertex or google-vertex-anthropic for Claude; leaves baseURL undefined when no host is configured so the SDK derives the aiplatform host
provider.id === 'cherryin'buildCherryinConfigmatches the provider id, not the resolved variant — the default chat endpoint resolves to cherryin-chat, so an id === 'cherryin' check never fires; async — resolves relay base URLs
id === 'newapi'buildNewApiConfig
id === 'aihubmix'buildAiHubMixConfigpasses the Chat, Responses, Anthropic, and Gemini URLs independently
id === 'dmxapi'buildDmxapiConfigpasses the Chat, Anthropic, and Gemini URLs independently
(no match)buildGenericProviderConfig / buildOpenAICompatibleConfiggeneric fallback

Several builders are async (Copilot token, CherryIN relay URLs), which is why providerToAiSdkConfig returns a promise.

Custom providers

src/main/ai/provider/custom/:

  • aihubmix — multi-vendor relay. Its model-id router selects the Anthropic, Gemini, OpenAI Responses, or OpenAI-compatible model and consumes the matching endpoint URL from provider settings.
  • dmxapi — multi-vendor relay. Its model-id router selects Anthropic, Gemini, OpenAI, or compatible models and consumes the matching endpoint URL.
  • newapi — same shape, different relay.

All three register through ProviderExtension.create(...) with their own AppProviderSettings shape.

Provider extensions

src/main/ai/provider/extensions.ts registers every @ai-sdk/* package Cherry uses with ProviderExtension.create. Each extension declares:

  • name (the AppProviderId for the base)
  • aliases (alternate ids that normalize to name)
  • variants (suffix entries — see above)
  • create (the SDK's factory)
  • toolFactories (per-capability factory functions for webSearch / urlContext etc.)
  • supportsImageGeneration (boolean flag)

Where to read more

  • Code: src/main/ai/provider/
  • Tests: provider/__tests__/endpoint.test.ts (54 cases)
  • Migration of legacy provider rows: src/main/data/migration/v2/migrators/mappings/ProviderModelMappings.ts
  • Catalog (new installs): packages/provider-registry/data/providers.json
  • Design: Adapter Family