Skip to content

refactor(ai): simplify provider boundaries - #43491

Closed
kitlangton wants to merge 6 commits into
v2from
ai-doc-cleanup
Closed

refactor(ai): simplify provider boundaries#43491
kitlangton wants to merge 6 commits into
v2from
ai-doc-cleanup

Conversation

@kitlangton

@kitlangton kitlangton commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

What

  • Remove stale packages/ai design/status/call-site documents and make the README, tutorial, and contributor guide describe the current route-based API.
  • Flatten provider-specific request options so the selected LanguageModel<Options> supplies type safety without repeating a provider namespace at runtime.
  • Replace the native provider package model(modelID, settings) boundary with model({ id, settings, credential, defaults }).
  • Keep credential selection and OAuth refresh in Core while moving credential-to-auth interpretation into each AI provider package.

Before / After

Before

LLM.request({
  model,
  providerOptions: {
    openai: {
      reasoningEffort: "high",
    },
  },
})

providerPackage.model(modelID, {
  ...settings,
  apiKey: coreMappedCredential,
  headers,
  body,
  limits,
})

Core had to know that Anthropic OAuth used authToken, Vertex OAuth used accessToken, and most other providers used apiKey. Legacy Anthropic OAuth could consequently lose its OAuth identity and be sent through x-api-key.

After

LLM.request({
  model,
  providerOptions: {
    reasoningEffort: "high",
  },
})

providerPackage.model({
  id: modelID,
  settings,
  credential: {
    type: "oauth",
    accessToken,
  },
  defaults: {
    headers,
    body,
    limits,
  },
})

The selected model still carries the provider-specific option type. At runtime one selected route decodes one flat option record. Native provider packages receive a neutral key | oauth credential and decide whether it becomes bearer auth, x-api-key, x-goog-api-key, Azure api-key, or another provider-owned scheme.

How

  • packages/ai/src/schema/options.ts makes canonical request options a flat JSON record while retaining the LanguageModel<Options> phantom type and deep precedence merge.
  • Provider protocols decode request.providerOptions directly. Namespaced provider metadata remains unchanged for durable replay and multi-layer provenance.
  • packages/ai/src/provider-package.ts defines the structured package input, neutral credential algebra, and shared route-default projection.
  • Native provider entrypoints interpret credentials locally and preserve selected-credential precedence over configured or ambient auth.
  • packages/core/src/model-resolver.ts passes credentials without package-name switches.
  • packages/core/src/aisdk-native.ts maps legacy OpenAI, Anthropic, OpenAI-compatible, Azure, Google, Vertex, Bedrock, OpenRouter, and xAI descriptors into the native package contract.
  • packages/core/src/aisdk.ts re-namespaces flat canonical request options only when calling the legacy Vercel AI SDK; message-part provider metadata remains namespaced.

Scope

  • Provider metadata is intentionally still namespaced because one durable message can contain metadata from multiple provider or gateway layers.
  • key | oauth models credentials selected by OpenCode integrations. AWS SigV4 credentials, service-account configuration, and ambient cloud credential chains remain provider settings.
  • Arbitrary AI SDK packages without a native mapping continue through the legacy adapter.
  • No Protocol or Server HttpApi surface changes.

Testing

  • bun typecheck in packages/ai
  • bun run build in packages/ai
  • Changed-path AI suites: 158 passed
  • bun typecheck in packages/core
  • Full Core suite: 1884 passed, 16 skipped
  • Pre-push workspace typecheck: 33 Turbo tasks passed
  • Prettier and git diff --check

The full AI suite reports 515 passed, 28 skipped, and 7 unrelated existing failures:

  • Azure Chat constructs /openai/v1/v1/chat/completions instead of /openai/v1/chat/completions.
  • Two OpenRouter recorded fixtures do not include the route's current default usage.include body field.
  • Four OpenRouter reasoning assertions expect content: null and omit explicit undefined compatibility fields, while current lowering emits content: "" with those fields.

Flow

flowchart LR
  Catalog[Catalog model] --> Legacy[Legacy descriptor mapping]
  Legacy --> Package[Native provider package]
  Integration[Integration connection] --> Resolve[Select and refresh credential]
  Resolve --> Package
  Package --> Model[LanguageModel with typed options]
  Model --> Request[Flat LLMRequest provider options]
  Request --> Route[Selected route decodes options]
  Route --> HTTP[Provider HTTP request]
Loading

@kitlangton

Copy link
Copy Markdown
Contributor Author

Split into three focused PRs:

Closing this combined PR in favor of those independently reviewable units.

@kitlangton kitlangton closed this Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant