feat(core): load native provider packages

This commit is contained in:
Shoubhit Dash 2026-06-25 20:24:53 +05:30
commit cd3885910d
66 changed files with 588 additions and 693 deletions

View file

@ -198,23 +198,23 @@ export const locationLayer = Layer.effect(
const key = `${model.providerID}/${model.id}/${model.modelID ?? model.id}/${JSON.stringify(model.settings)}`
const existing = languages.get(key)
if (existing) return existing
if (!model.aisdk)
if (!ProviderV2.isAISDK(model.package))
return yield* new InitError({
providerID: model.providerID,
cause: new Error(`Unsupported package ${model.package}`),
})
const options = prepareOptions(model, model.package ?? "")
const packageName = ProviderV2.packageName(model.package) ?? ""
const options = prepareOptions(model, packageName)
const sdkKey = JSON.stringify({
providerID: model.providerID,
package: model.package,
package: packageName,
settings: model.settings,
options,
})
const sdk =
sdks.get(sdkKey) ??
(yield* service.runSDK({ model, package: model.package ?? "", options }).pipe(initError(model.providerID)))
.sdk
(yield* service.runSDK({ model, package: packageName, options }).pipe(initError(model.providerID))).sdk
if (!sdk)
return yield* new InitError({
providerID: model.providerID,

View file

@ -7,6 +7,7 @@ import { EventV2 } from "./event"
import { Policy } from "./policy"
import { State } from "./state"
import { Integration } from "./integration"
import { ProviderOverlay } from "./provider-overlay"
export type ProviderRecord = {
provider: ProviderV2.MutableInfo
@ -79,10 +80,9 @@ export const layer = Layer.effect(
return ModelV2.Info.make({
...model,
package: model.package ?? provider.package,
aisdk: model.package === undefined ? (model.aisdk ?? provider.aisdk) : model.aisdk,
settings: merge(provider.settings, model.settings),
headers: headers(provider.headers, model.headers),
body: merge(provider.body, model.body),
settings: ProviderOverlay.merge(provider.settings, model.settings),
headers: ProviderOverlay.headers(provider.headers, model.headers),
body: ProviderOverlay.merge(provider.body, model.body),
})
}
@ -270,43 +270,6 @@ export const layer = Layer.effect(
const SMALL_MODEL_RE = /\b(nano|flash|lite|mini|haiku|small|fast)\b/
function merge(
base: Readonly<Record<string, unknown>> | undefined,
overlay: Readonly<Record<string, unknown>> | undefined,
): Record<string, unknown> | undefined {
if (base === undefined) return overlay && { ...overlay }
if (overlay === undefined) return { ...base }
return Object.fromEntries(
new Set([...Object.keys(base), ...Object.keys(overlay)]).values().map((key) => {
const left = base[key]
const right = overlay[key]
if (right === undefined) return [key, left]
if (plain(left) && plain(right)) return [key, merge(left, right)]
return [key, right]
}),
)
}
function plain(input: unknown): input is Readonly<Record<string, unknown>> {
if (typeof input !== "object" || input === null || Array.isArray(input)) return false
const prototype = Object.getPrototypeOf(input)
return prototype === Object.prototype || prototype === null
}
function headers(
base: Readonly<Record<string, string>> | undefined,
overlay: Readonly<Record<string, string>> | undefined,
) {
return Object.fromEntries(
[...Object.entries(base ?? {}), ...Object.entries(overlay ?? {})]
.reduce((result, entry) => {
result.set(entry[0].toLowerCase(), entry)
return result
}, new Map<string, [string, string]>())
.values(),
)
}
export const locationLayer = layer.pipe(
Layer.provideMerge(Integration.locationLayer),
Layer.provideMerge(Policy.locationLayer),

View file

@ -4,6 +4,7 @@ import { define } from "../../plugin/internal"
import { Effect } from "effect"
import { Config } from "../../config"
import { ModelV2 } from "../../model"
import { ProviderOverlay } from "../../provider-overlay"
import { ProviderV2 } from "../../provider"
export const Plugin = define({
@ -53,10 +54,10 @@ export const Plugin = define({
catalog.provider.update(providerID, (provider) => {
if (item.name !== undefined) provider.name = item.name
if (item.package !== undefined) provider.package = item.package
if (item.aisdk !== undefined) provider.aisdk = item.aisdk
if (item.settings !== undefined) provider.settings = { ...provider.settings, ...item.settings }
if (item.headers !== undefined) provider.headers = { ...provider.headers, ...item.headers }
if (item.body !== undefined) provider.body = { ...provider.body, ...item.body }
if (item.settings !== undefined)
provider.settings = ProviderOverlay.merge(provider.settings, item.settings)
if (item.headers !== undefined) provider.headers = ProviderOverlay.headers(provider.headers, item.headers)
if (item.body !== undefined) provider.body = ProviderOverlay.merge(provider.body, item.body)
})
for (const [id, config] of Object.entries(item.models ?? {})) {
catalog.model.update(providerID, id, (model) => {
@ -64,10 +65,10 @@ export const Plugin = define({
if (config.name !== undefined) model.name = config.name
if (config.modelID !== undefined) model.modelID = config.modelID
if (config.package !== undefined) model.package = config.package
if (config.aisdk !== undefined) model.aisdk = config.aisdk
if (config.settings !== undefined) model.settings = { ...model.settings, ...config.settings }
if (config.headers !== undefined) model.headers = { ...model.headers, ...config.headers }
if (config.body !== undefined) model.body = { ...model.body, ...config.body }
if (config.settings !== undefined)
model.settings = ProviderOverlay.merge(model.settings, config.settings)
if (config.headers !== undefined) model.headers = ProviderOverlay.headers(model.headers, config.headers)
if (config.body !== undefined) model.body = ProviderOverlay.merge(model.body, config.body)
if (config.capabilities !== undefined) {
model.capabilities = {
tools: config.capabilities.tools,
@ -86,9 +87,10 @@ export const Plugin = define({
model.variants.push(existing)
}
if (variant.settings !== undefined)
existing.settings = { ...existing.settings, ...variant.settings }
if (variant.headers !== undefined) existing.headers = { ...existing.headers, ...variant.headers }
if (variant.body !== undefined) existing.body = { ...existing.body, ...variant.body }
existing.settings = ProviderOverlay.merge(existing.settings, variant.settings)
if (variant.headers !== undefined)
existing.headers = ProviderOverlay.headers(existing.headers, variant.headers)
if (variant.body !== undefined) existing.body = ProviderOverlay.merge(existing.body, variant.body)
}
}
if (config.cost !== undefined) {

View file

@ -40,7 +40,6 @@ class Model extends Schema.Class<Model>("ConfigV2.Model")({
family: ModelV2.Family.pipe(Schema.optional),
name: Schema.String.pipe(Schema.optional),
package: Schema.String.pipe(Schema.optional),
aisdk: Schema.Literal(true).pipe(Schema.optional),
...Overlays,
capabilities: ModelV2.Capabilities.pipe(Schema.optional),
variants: Schema.Struct({
@ -56,7 +55,6 @@ export class Info extends Schema.Class<Info>("ConfigV2.Provider")({
name: Schema.String.pipe(Schema.optional),
env: Schema.String.pipe(Schema.Array, Schema.optional),
package: Schema.String.pipe(Schema.optional),
aisdk: Schema.Literal(true).pipe(Schema.optional),
...Overlays,
models: Schema.Record(Schema.String, Model).pipe(Schema.optional),
}) {}

View file

@ -1,6 +1,6 @@
import { Types } from "effect"
import { Model } from "@opencode-ai/schema/model"
import { ProviderV2 } from "./provider"
import type { DeepMutable } from "./schema"
export const ID = Model.ID
export type ID = typeof ID.Type
@ -23,7 +23,7 @@ export type Ref = typeof Ref.Type
export const Info = Model.Info
export type Info = Model.Info
export type MutableInfo = Types.DeepMutable<Info>
export type MutableInfo = DeepMutable<Info>
export function parse(input: string): { providerID: ProviderV2.ID; modelID: ID } {
const [providerID, ...modelID] = input.split("/")

View file

@ -75,8 +75,7 @@ export const ModelsDevPlugin = define({
const providerID = ProviderV2.ID.make(item.id)
catalog.provider.update(providerID, (provider) => {
provider.name = item.name
provider.package = item.npm ?? ""
provider.aisdk = item.npm ? true : undefined
provider.package = item.npm ? ProviderV2.aisdk(item.npm) : ""
provider.settings = item.api ? { ...provider.settings, baseURL: item.api } : provider.settings
})
@ -85,8 +84,7 @@ export const ModelsDevPlugin = define({
catalog.model.update(providerID, modelID, (draft) => {
draft.name = model.name
draft.family = model.family ? ModelV2.Family.make(model.family) : undefined
draft.package = model.provider?.npm
draft.aisdk = model.provider?.npm ? true : undefined
draft.package = model.provider?.npm ? ProviderV2.aisdk(model.provider.npm) : undefined
draft.settings = model.provider?.api ? { ...draft.settings, baseURL: model.provider.api } : draft.settings
draft.capabilities = {
tools: model.tool_call,

View file

@ -65,8 +65,8 @@ export const AmazonBedrockPlugin = define({
yield* ctx.catalog.transform(
Effect.fn(function* (evt) {
for (const item of evt.provider.list()) {
if (!item.provider.aisdk) continue
if (item.provider.package !== "@ai-sdk/amazon-bedrock") continue
if (!ProviderV2.isAISDK(item.provider.package)) continue
if (ProviderV2.packageName(item.provider.package) !== "@ai-sdk/amazon-bedrock") continue
evt.provider.update(item.provider.id, (provider) => {
if (typeof provider.settings?.endpoint !== "string") return
// The AI SDK expects a base URL, but users configure Bedrock private/VPC
@ -113,7 +113,10 @@ export const AmazonBedrockPlugin = define({
yield* ctx.aisdk.language(
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.amazonBedrock) return
if (evt.model.aisdk && evt.model.package === "@ai-sdk/amazon-bedrock/mantle") {
if (
ProviderV2.isAISDK(evt.model.package) &&
ProviderV2.packageName(evt.model.package) === "@ai-sdk/amazon-bedrock/mantle"
) {
evt.language = selectMantleModel(evt.sdk, evt.model.modelID ?? evt.model.id)
return
}

View file

@ -1,5 +1,6 @@
import { Effect } from "effect"
import { define } from "../internal"
import { ProviderV2 } from "../../provider"
export const AnthropicPlugin = define({
id: "anthropic",
@ -7,8 +8,8 @@ export const AnthropicPlugin = define({
yield* ctx.catalog.transform(
Effect.fn(function* (evt) {
for (const item of evt.provider.list()) {
if (!item.provider.aisdk) continue
if (item.provider.package !== "@ai-sdk/anthropic") continue
if (!ProviderV2.isAISDK(item.provider.package)) continue
if (ProviderV2.packageName(item.provider.package) !== "@ai-sdk/anthropic") continue
evt.provider.update(item.provider.id, (provider) => {
provider.headers = {
...provider.headers,

View file

@ -16,8 +16,8 @@ export const AzurePlugin = define({
yield* ctx.catalog.transform(
Effect.fn(function* (evt) {
for (const item of evt.provider.list()) {
if (!item.provider.aisdk) continue
if (item.provider.package !== "@ai-sdk/azure") continue
if (!ProviderV2.isAISDK(item.provider.package)) continue
if (ProviderV2.packageName(item.provider.package) !== "@ai-sdk/azure") continue
const configured = item.provider.settings?.resourceName
const resourceName =
typeof configured === "string" && configured.trim() !== "" ? configured : process.env.AZURE_RESOURCE_NAME
@ -35,7 +35,7 @@ export const AzurePlugin = define({
if (
!evt.options.resourceName &&
!evt.options.baseURL &&
(!evt.model.aisdk || typeof evt.model.settings?.baseURL !== "string")
(!ProviderV2.isAISDK(evt.model.package) || typeof evt.model.settings?.baseURL !== "string")
) {
throw new Error(
"AZURE_RESOURCE_NAME is missing, set it using env var or reconnecting the azure provider and setting it",
@ -67,8 +67,8 @@ export const AzureCognitiveServicesPlugin = define({
const resourceName = process.env.AZURE_COGNITIVE_SERVICES_RESOURCE_NAME
if (!resourceName) return
for (const item of evt.provider.list()) {
if (!item.provider.aisdk) continue
if (item.provider.package !== "@ai-sdk/openai-compatible") continue
if (!ProviderV2.isAISDK(item.provider.package)) continue
if (ProviderV2.packageName(item.provider.package) !== "@ai-sdk/openai-compatible") continue
if (!item.provider.id.includes("azure-cognitive-services")) continue
evt.provider.update(item.provider.id, (provider) => {
provider.settings = {

View file

@ -1,5 +1,6 @@
import { Effect } from "effect"
import { define } from "../internal"
import { ProviderV2 } from "../../provider"
export const CerebrasPlugin = define({
id: "cerebras",
@ -7,8 +8,8 @@ export const CerebrasPlugin = define({
yield* ctx.catalog.transform(
Effect.fn(function* (evt) {
for (const item of evt.provider.list()) {
if (!item.provider.aisdk) continue
if (item.provider.package !== "@ai-sdk/cerebras") continue
if (!ProviderV2.isAISDK(item.provider.package)) continue
if (ProviderV2.packageName(item.provider.package) !== "@ai-sdk/cerebras") continue
evt.provider.update(item.provider.id, (provider) => {
provider.headers = { ...provider.headers, "X-Cerebras-3rd-Party-Integration": "opencode" }
})

View file

@ -14,7 +14,7 @@ export const CloudflareWorkersAIPlugin = define({
const item = evt.provider.get(providerID)
if (!item) return
evt.provider.update(item.provider.id, (provider) => {
if (!provider.aisdk) return
if (!ProviderV2.isAISDK(provider.package)) return
if (typeof provider.settings?.baseURL === "string") return
const accountId = resolveAccountId(provider.settings ?? {})
if (accountId) provider.settings = { ...provider.settings, baseURL: workersEndpoint(accountId) }
@ -54,8 +54,11 @@ function workersEndpoint(accountId: string) {
return `https://api.cloudflare.com/client/v4/accounts/${accountId}/ai/v1`
}
function hasWorkersEndpoint(model: { readonly aisdk?: true; readonly settings?: Readonly<Record<string, unknown>> }) {
return model.aisdk && typeof model.settings?.baseURL === "string"
function hasWorkersEndpoint(model: {
readonly package?: string
readonly settings?: Readonly<Record<string, unknown>>
}) {
return ProviderV2.isAISDK(model.package) && typeof model.settings?.baseURL === "string"
}
function sdkOptions(options: Record<string, any>) {

View file

@ -60,12 +60,12 @@ export const GoogleVertexPlugin = define({
yield* ctx.catalog.transform(
Effect.fn(function* (evt) {
for (const item of evt.provider.list()) {
if (!item.provider.aisdk) continue
if (!ProviderV2.isAISDK(item.provider.package)) continue
if (
item.provider.package !== "@ai-sdk/google-vertex" &&
ProviderV2.packageName(item.provider.package) !== "@ai-sdk/google-vertex" &&
!(
item.provider.id === ProviderV2.ID.googleVertex &&
item.provider.package.includes("@ai-sdk/openai-compatible")
ProviderV2.packageName(item.provider.package)?.includes("@ai-sdk/openai-compatible")
)
)
continue
@ -79,7 +79,7 @@ export const GoogleVertexPlugin = define({
...(typeof provider.settings?.baseURL === "string"
? { baseURL: replaceVertexVars(provider.settings.baseURL, project, location) }
: {}),
...(provider.package.includes("@ai-sdk/openai-compatible")
...(ProviderV2.packageName(provider.package)?.includes("@ai-sdk/openai-compatible")
? { fetch: authFetch(provider.settings?.fetch) }
: {}),
}
@ -121,8 +121,8 @@ export const GoogleVertexAnthropicPlugin = define({
yield* ctx.catalog.transform(
Effect.fn(function* (evt) {
for (const item of evt.provider.list()) {
if (!item.provider.aisdk) continue
if (item.provider.package !== "@ai-sdk/google-vertex/anthropic") continue
if (!ProviderV2.isAISDK(item.provider.package)) continue
if (ProviderV2.packageName(item.provider.package) !== "@ai-sdk/google-vertex/anthropic") continue
const project =
item.provider.settings?.project ??
process.env.GOOGLE_CLOUD_PROJECT ??

View file

@ -1,5 +1,6 @@
import { Effect } from "effect"
import { define } from "../internal"
import { ProviderV2 } from "../../provider"
export const KiloPlugin = define({
id: "kilo",
@ -7,8 +8,8 @@ export const KiloPlugin = define({
yield* ctx.catalog.transform(
Effect.fn(function* (evt) {
for (const item of evt.provider.list()) {
if (!item.provider.aisdk) continue
if (item.provider.package !== "@ai-sdk/openai-compatible") continue
if (!ProviderV2.isAISDK(item.provider.package)) continue
if (ProviderV2.packageName(item.provider.package) !== "@ai-sdk/openai-compatible") continue
if (item.provider.settings?.baseURL !== "https://api.kilo.ai/api/gateway") continue
evt.provider.update(item.provider.id, (provider) => {
provider.headers = { ...provider.headers, "HTTP-Referer": "https://opencode.ai/", "X-Title": "opencode" }

View file

@ -1,6 +1,7 @@
import { Effect } from "effect"
import { define } from "../internal"
import { Integration } from "../../integration"
import { ProviderV2 } from "../../provider"
export const LLMGatewayPlugin = define({
id: "llmgateway",
@ -10,8 +11,8 @@ export const LLMGatewayPlugin = define({
Effect.fn(function* (evt) {
for (const item of evt.provider.list()) {
if (item.provider.disabled) continue
if (!item.provider.aisdk) continue
if (item.provider.package !== "@ai-sdk/openai-compatible") continue
if (!ProviderV2.isAISDK(item.provider.package)) continue
if (ProviderV2.packageName(item.provider.package) !== "@ai-sdk/openai-compatible") continue
if (item.provider.settings?.baseURL !== "https://api.llmgateway.io/v1") continue
if (!(yield* integrations.get(Integration.ID.make(item.provider.id)))) continue
evt.provider.update(item.provider.id, (provider) => {

View file

@ -1,5 +1,6 @@
import { Effect } from "effect"
import { define } from "../internal"
import { ProviderV2 } from "../../provider"
export const NvidiaPlugin = define({
id: "nvidia",
@ -7,8 +8,8 @@ export const NvidiaPlugin = define({
yield* ctx.catalog.transform(
Effect.fn(function* (evt) {
for (const item of evt.provider.list()) {
if (!item.provider.aisdk) continue
if (item.provider.package !== "@ai-sdk/openai-compatible") continue
if (!ProviderV2.isAISDK(item.provider.package)) continue
if (ProviderV2.packageName(item.provider.package) !== "@ai-sdk/openai-compatible") continue
if (item.provider.settings?.baseURL !== "https://integrate.api.nvidia.com/v1") continue
evt.provider.update(item.provider.id, (provider) => {
provider.headers = {

View file

@ -156,8 +156,8 @@ export const OpenAIPlugin = define({
yield* ctx.catalog.transform(
Effect.fn(function* (evt) {
for (const item of evt.provider.list()) {
if (!item.provider.aisdk) continue
if (item.provider.package !== "@ai-sdk/openai") continue
if (!ProviderV2.isAISDK(item.provider.package)) continue
if (ProviderV2.packageName(item.provider.package) !== "@ai-sdk/openai") continue
if (!item.models.has(ModelV2.ID.make("gpt-5-chat-latest"))) continue
evt.model.update(item.provider.id, ModelV2.ID.make("gpt-5-chat-latest"), (model) => {
// OpenAIPlugin sends OpenAI models through Responses; this alias is a

View file

@ -111,8 +111,7 @@ export const OpencodePlugin = define<HttpClient.HttpClient | EventV2.Service | S
catalog.provider.update(providerID, (provider) => {
provider.integrationID = Integration.ID.make("opencode")
if (item.name !== undefined) provider.name = item.name
provider.package = item.npm ?? ""
provider.aisdk = item.npm ? true : undefined
provider.package = item.npm ? ProviderV2.aisdk(item.npm) : ""
provider.settings = {
...provider.settings,
...withoutCredentials(item.options),
@ -132,8 +131,7 @@ export const OpencodePlugin = define<HttpClient.HttpClient | EventV2.Service | S
if (config.name !== undefined) model.name = config.name
if (config.id !== undefined) model.modelID = config.id
if (config.provider !== undefined) {
model.package = config.provider.npm
model.aisdk = config.provider.npm ? true : undefined
model.package = config.provider.npm ? ProviderV2.aisdk(config.provider.npm) : undefined
if (config.provider.api) model.settings = { ...model.settings, baseURL: config.provider.api }
}
if (config.tool_call !== undefined) model.capabilities.tools = config.tool_call

View file

@ -1,5 +1,6 @@
import { Effect } from "effect"
import { ModelV2 } from "../../model"
import { ProviderV2 } from "../../provider"
import { define } from "../internal"
export const OpenRouterPlugin = define({
@ -8,8 +9,8 @@ export const OpenRouterPlugin = define({
yield* ctx.catalog.transform(
Effect.fn(function* (evt) {
for (const item of evt.provider.list()) {
if (!item.provider.aisdk) continue
if (item.provider.package !== "@openrouter/ai-sdk-provider") continue
if (!ProviderV2.isAISDK(item.provider.package)) continue
if (ProviderV2.packageName(item.provider.package) !== "@openrouter/ai-sdk-provider") continue
evt.provider.update(item.provider.id, (provider) => {
provider.headers = { ...provider.headers, "HTTP-Referer": "https://opencode.ai/", "X-Title": "opencode" }
})

View file

@ -1,5 +1,6 @@
import { Effect } from "effect"
import { define } from "../internal"
import { ProviderV2 } from "../../provider"
export const VercelPlugin = define({
id: "vercel",
@ -7,8 +8,8 @@ export const VercelPlugin = define({
yield* ctx.catalog.transform(
Effect.fn(function* (evt) {
for (const item of evt.provider.list()) {
if (!item.provider.aisdk) continue
if (item.provider.package !== "@ai-sdk/vercel") continue
if (!ProviderV2.isAISDK(item.provider.package)) continue
if (ProviderV2.packageName(item.provider.package) !== "@ai-sdk/vercel") continue
evt.provider.update(item.provider.id, (provider) => {
provider.headers = { ...provider.headers, "http-referer": "https://opencode.ai/", "x-title": "opencode" }
})

View file

@ -1,5 +1,6 @@
import { Effect } from "effect"
import { define } from "../internal"
import { ProviderV2 } from "../../provider"
export const ZenmuxPlugin = define({
id: "zenmux",
@ -7,8 +8,8 @@ export const ZenmuxPlugin = define({
yield* ctx.catalog.transform(
Effect.fn(function* (evt) {
for (const item of evt.provider.list()) {
if (!item.provider.aisdk) continue
if (item.provider.package !== "@ai-sdk/openai-compatible") continue
if (!ProviderV2.isAISDK(item.provider.package)) continue
if (ProviderV2.packageName(item.provider.package) !== "@ai-sdk/openai-compatible") continue
if (item.provider.settings?.baseURL !== "https://zenmux.ai/api/v1") continue
evt.provider.update(item.provider.id, (provider) => {
provider.headers = {

View file

@ -2,6 +2,7 @@ export * as VariantPlugin from "./variant"
import { Effect } from "effect"
import { ModelV2 } from "../model"
import { ProviderV2 } from "../provider"
import { define } from "./internal"
export const Plugin = define({
@ -29,9 +30,10 @@ export const Plugin = define({
export function generate(
model: ModelV2.Info,
provider?: { readonly package: string; readonly aisdk?: true },
provider?: { readonly package: string },
): NonNullable<ModelV2.Info["variants"]> {
if (!(model.aisdk ?? provider?.aisdk) || (model.package ?? provider?.package) !== "@ai-sdk/openai-compatible") return []
const packageName = model.package ?? provider?.package
if (!ProviderV2.isAISDK(packageName) || ProviderV2.packageName(packageName) !== "@ai-sdk/openai-compatible") return []
const ids = `${model.id} ${model.modelID ?? ""}`.toLowerCase()
if (!["glm-5.2", "glm-5-2", "glm-5p2"].some((name) => ids.includes(name))) return []
return ["high", "max"].map((id) => ({

View file

@ -0,0 +1,38 @@
export * as ProviderOverlay from "./provider-overlay"
export function merge(
base: Readonly<Record<string, unknown>> | undefined,
overlay: Readonly<Record<string, unknown>> | undefined,
): Record<string, unknown> | undefined {
if (base === undefined) return overlay && { ...overlay }
if (overlay === undefined) return { ...base }
return Object.fromEntries(
new Set([...Object.keys(base), ...Object.keys(overlay)]).values().map((key) => {
const left = base[key]
const right = overlay[key]
if (right === undefined) return [key, left]
if (plain(left) && plain(right)) return [key, merge(left, right)]
return [key, right]
}),
)
}
export function headers(
base: Readonly<Record<string, string>> | undefined,
overlay: Readonly<Record<string, string>> | undefined,
) {
return Object.fromEntries(
[...Object.entries(base ?? {}), ...Object.entries(overlay ?? {})]
.reduce((result, entry) => {
result.set(entry[0].toLowerCase(), entry)
return result
}, new Map<string, [string, string]>())
.values(),
)
}
function plain(input: unknown): input is Readonly<Record<string, unknown>> {
if (typeof input !== "object" || input === null || Array.isArray(input)) return false
const prototype = Object.getPrototypeOf(input)
return prototype === Object.prototype || prototype === null
}

View file

@ -0,0 +1,75 @@
export * as ProviderPackage from "./provider-package"
import { Effect, Option, Schema } from "effect"
import { pathToFileURL } from "url"
import type { Model, ProviderPackageDefinition, ProviderPackageSettings } from "@opencode-ai/llm"
import { Npm } from "./npm"
const cache = new Map<string, Promise<unknown>>()
export class LoadError extends Schema.TaggedErrorClass<LoadError>()("ProviderPackage.LoadError", {
package: Schema.String,
cause: Schema.Defect(),
}) {}
export const load = Effect.fn("ProviderPackage.load")(function* (specifier: string) {
const npm = Option.getOrUndefined(yield* Effect.serviceOption(Npm.Service))
const resolved =
specifier.startsWith("file://") || specifier.startsWith("@opencode-ai/llm/")
? specifier
: yield* Effect.sync(() => {
try {
return import.meta.resolve(specifier)
} catch {
return undefined
}
})
if (resolved) return yield* importProviderPackage(specifier, resolved)
if (!npm) {
return yield* new LoadError({
package: specifier,
cause: new Error(`Provider package ${specifier} is not installed`),
})
}
const installed = yield* npm
.add(packageName(specifier))
.pipe(Effect.mapError((cause) => new LoadError({ package: specifier, cause })))
const entrypoint = yield* Effect.try({
try: () => import.meta.resolve(specifier, pathToFileURL(`${installed.directory}/`).href),
catch: (cause) => new LoadError({ package: specifier, cause }),
})
return yield* importProviderPackage(specifier, entrypoint)
})
const importProviderPackage = Effect.fn("ProviderPackage.import")(function* (specifier: string, entrypoint: string) {
const module = yield* Effect.tryPromise({
try: () => {
const existing = cache.get(entrypoint)
if (existing) return existing
const loaded = import(entrypoint)
cache.set(entrypoint, loaded)
return loaded
},
catch: (cause) => new LoadError({ package: specifier, cause }),
})
if (!isProviderPackage(module)) {
return yield* new LoadError({
package: specifier,
cause: new Error(`Provider package ${specifier} does not export model(id, settings)`),
})
}
return module
})
export const make = (module: ProviderPackageDefinition, modelID: string, settings: ProviderPackageSettings): Model =>
module.model(modelID, settings)
function isProviderPackage(input: unknown): input is ProviderPackageDefinition {
return typeof input === "object" && input !== null && "model" in input && typeof input.model === "function"
}
function packageName(specifier: string) {
const parts = specifier.split("/")
if (specifier.startsWith("@")) return parts.slice(0, 2).join("/")
return parts[0]
}

View file

@ -1,15 +1,21 @@
export * as ProviderV2 from "./provider"
import { Types } from "effect"
import { Provider } from "@opencode-ai/schema/provider"
import type { DeepMutable } from "./schema"
export const ID = Provider.ID
export type ID = typeof ID.Type
export const AISDK_PREFIX = "aisdk:"
export const isAISDK = (packageName: string | undefined) => packageName?.startsWith(AISDK_PREFIX) ?? false
export const aisdk = (packageName: string) => (isAISDK(packageName) ? packageName : `${AISDK_PREFIX}${packageName}`)
export const packageName = (packageName: string | undefined) =>
isAISDK(packageName) ? packageName!.slice(AISDK_PREFIX.length) : packageName
export const Request = Provider.Request
export type Request = Provider.Request
export const Info = Provider.Info
export type Info = Provider.Info
export type MutableInfo = Types.DeepMutable<Info>
export type MutableInfo = DeepMutable<Info>

View file

@ -12,6 +12,8 @@ import { Credential } from "../../credential"
import { Integration } from "../../integration"
import { ModelV2 } from "../../model"
import { ProviderV2 } from "../../provider"
import { ProviderPackage } from "../../provider-package"
import { ProviderOverlay } from "../../provider-overlay"
import { SessionSchema } from "../schema"
export class ModelNotSelectedError extends Schema.TaggedErrorClass<ModelNotSelectedError>()(
@ -117,9 +119,9 @@ const withVariant = (
return Effect.succeed(
variant
? produce(model, (draft) => {
draft.settings = merge(draft.settings, variant.settings)
draft.headers = { ...draft.headers, ...variant.headers }
draft.body = merge(draft.body, variant.body)
draft.settings = ProviderOverlay.merge(draft.settings, variant.settings)
draft.headers = ProviderOverlay.headers(draft.headers, variant.headers)
draft.body = ProviderOverlay.merge(draft.body, variant.body)
})
: model,
)
@ -133,17 +135,18 @@ export const fromCatalogModel = (
credential?.metadata === undefined
? model
: produce(model, (draft) => {
draft.settings = { ...draft.settings, ...credential.metadata }
draft.settings = ProviderOverlay.merge(draft.settings, credential.metadata)
})
const key = apiKey(resolved, credential)
if (resolved.aisdk && resolved.package === "@ai-sdk/openai") {
const packageName = ProviderV2.packageName(resolved.package)
if (ProviderV2.isAISDK(resolved.package) && packageName === "@ai-sdk/openai") {
return Effect.succeed(
withDefaults(resolved, OpenAIResponses.route)
.with({ auth: key === undefined ? Auth.none : Auth.bearer(key) })
.model({ id: resolved.modelID ?? resolved.id }),
)
}
if (resolved.aisdk && resolved.package === "@ai-sdk/anthropic") {
if (ProviderV2.isAISDK(resolved.package) && packageName === "@ai-sdk/anthropic") {
return Effect.succeed(
withDefaults(resolved, AnthropicMessages.route)
.with({ auth: key === undefined ? Auth.none : Auth.header("x-api-key", key) })
@ -151,8 +154,8 @@ export const fromCatalogModel = (
)
}
if (
resolved.aisdk &&
resolved.package === "@ai-sdk/openai-compatible" &&
ProviderV2.isAISDK(resolved.package) &&
packageName === "@ai-sdk/openai-compatible" &&
typeof resolved.settings?.baseURL === "string"
) {
return Effect.succeed(
@ -161,11 +164,44 @@ export const fromCatalogModel = (
.model({ id: resolved.modelID ?? resolved.id }),
)
}
if (!ProviderV2.isAISDK(resolved.package) && resolved.package) {
const specifier = resolved.package
return Effect.gen(function* () {
const module = yield* ProviderPackage.load(specifier).pipe(
Effect.mapError(
() =>
new UnsupportedPackageError({
providerID: resolved.providerID,
modelID: resolved.id,
package: specifier,
}),
),
)
const settings = {
...resolved.settings,
...(credential?.type === "key" ? { apiKey: credential.key } : {}),
...(credential?.type === "oauth" ? { apiKey: credential.access } : {}),
...credential?.metadata,
headers: resolved.headers,
body: resolved.body,
limits: { context: resolved.limit.context, output: resolved.limit.output },
}
return yield* Effect.try({
try: () => ProviderPackage.make(module, resolved.modelID ?? resolved.id, settings),
catch: () =>
new UnsupportedPackageError({
providerID: resolved.providerID,
modelID: resolved.id,
package: specifier,
}),
})
})
}
return Effect.fail(
new UnsupportedPackageError({
providerID: resolved.providerID,
modelID: resolved.id,
package: resolved.aisdk ? `aisdk:${resolved.package}` : (resolved.package ?? "unknown"),
package: resolved.package ?? "unknown",
}),
)
}
@ -174,10 +210,12 @@ export const resolve = (session: SessionSchema.Info, model: ModelV2.Info, creden
withVariant(model, session.model?.variant).pipe(Effect.flatMap((model) => fromCatalogModel(model, credential)))
export const supported = (model: ModelV2.Info) =>
model.aisdk === true &&
(model.package === "@ai-sdk/openai" ||
model.package === "@ai-sdk/anthropic" ||
(model.package === "@ai-sdk/openai-compatible" && typeof model.settings?.baseURL === "string"))
Boolean(model.package) &&
(!ProviderV2.isAISDK(model.package) ||
ProviderV2.packageName(model.package) === "@ai-sdk/openai" ||
ProviderV2.packageName(model.package) === "@ai-sdk/anthropic" ||
(ProviderV2.packageName(model.package) === "@ai-sdk/openai-compatible" &&
typeof model.settings?.baseURL === "string"))
/** Resolves models from the catalog belonging to the current Location runtime. */
export const locationLayer = Layer.effect(
@ -215,26 +253,3 @@ export const locationLayer = Layer.effect(
})
}),
)
function merge(
base: Readonly<Record<string, unknown>> | undefined,
overlay: Readonly<Record<string, unknown>> | undefined,
): Record<string, unknown> | undefined {
if (base === undefined) return overlay && { ...overlay }
if (overlay === undefined) return { ...base }
return Object.fromEntries(
Array.from(new Set([...Object.keys(base), ...Object.keys(overlay)])).map((key) => {
const left = base[key]
const right = overlay[key]
if (right === undefined) return [key, left]
if (plain(left) && plain(right)) return [key, merge(left, right)]
return [key, right]
}),
)
}
function plain(input: unknown): input is Readonly<Record<string, unknown>> {
if (typeof input !== "object" || input === null || Array.isArray(input)) return false
const prototype = Object.getPrototypeOf(input)
return prototype === Object.prototype || prototype === null
}

View file

@ -6,6 +6,7 @@ import { ConfigMCPV1 } from "./mcp"
import { ConfigPermissionV1 } from "./permission"
import { ConfigProviderV1 } from "./provider"
import { ConfigProviderOptionsV1 } from "./provider-options"
import { ProviderV2 } from "../../provider"
const keys = new Set([
"logLevel",
@ -173,8 +174,7 @@ function migrateProvider(info: ConfigProviderV1.Info) {
return {
name: info.name,
env: info.env,
package: info.npm,
aisdk: info.npm ? (true as const) : undefined,
package: info.npm ? ProviderV2.aisdk(info.npm) : undefined,
settings: info.api ? { ...options.settings, baseURL: info.api } : options.settings,
headers: info.options && options.headers,
body: info.options && options.body,
@ -213,8 +213,7 @@ function migrateModel(info: typeof ConfigProviderV1.Model.Type, packageName?: st
modelID: info.id,
family: info.family,
name: info.name,
package: info.provider?.npm,
aisdk: info.provider?.npm ? (true as const) : undefined,
package: info.provider?.npm ? ProviderV2.aisdk(info.provider.npm) : undefined,
settings: info.provider?.api ? { ...settings, baseURL: info.provider.api } : settings,
capabilities,
headers: info.headers,

View file

@ -141,15 +141,13 @@ describe("CatalogV2", () => {
const providerID = ProviderV2.ID.make("test")
yield* catalog.transform((catalog) =>
catalog.provider.update(providerID, (provider) => {
provider.package = "@ai-sdk/openai-compatible"
provider.aisdk = true
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
provider.settings = { baseURL: "https://override.example.com" }
}),
)
expect(required(yield* catalog.provider.get(providerID))).toMatchObject({
package: "@ai-sdk/openai-compatible",
aisdk: true,
package: "aisdk:@ai-sdk/openai-compatible",
settings: { baseURL: "https://override.example.com" },
})
}),
@ -162,8 +160,7 @@ describe("CatalogV2", () => {
const modelID = ModelV2.ID.make("model")
yield* catalog.transform((catalog) => {
catalog.provider.update(providerID, (provider) => {
provider.package = "@ai-sdk/openai-compatible"
provider.aisdk = true
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
provider.settings = { baseURL: "https://provider.example.com" }
})
catalog.model.update(providerID, modelID, (model) => {
@ -175,8 +172,7 @@ describe("CatalogV2", () => {
expect(required(yield* catalog.model.get(providerID, modelID))).toMatchObject({
id: modelID,
modelID: ModelV2.ID.make("upstream-model"),
package: "@ai-sdk/openai-compatible",
aisdk: true,
package: "aisdk:@ai-sdk/openai-compatible",
settings: { baseURL: "https://override.example.com" },
})
}),
@ -189,8 +185,7 @@ describe("CatalogV2", () => {
const modelID = ModelV2.ID.make("model")
yield* catalog.transform((catalog) => {
catalog.provider.update(providerID, (provider) => {
provider.package = "@ai-sdk/openai-compatible"
provider.aisdk = true
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
provider.settings = { baseURL: "https://provider.example.com" }
})
catalog.model.update(providerID, modelID, () => {})
@ -198,8 +193,7 @@ describe("CatalogV2", () => {
expect(required(yield* catalog.model.get(providerID, modelID))).toMatchObject({
id: modelID,
package: "@ai-sdk/openai-compatible",
aisdk: true,
package: "aisdk:@ai-sdk/openai-compatible",
settings: { baseURL: "https://provider.example.com" },
})
}),
@ -212,18 +206,36 @@ describe("CatalogV2", () => {
const modelID = ModelV2.ID.make("model")
yield* catalog.transform((catalog) => {
catalog.provider.update(providerID, (provider) => {
provider.headers = { provider: "provider", shared: "provider" }
provider.body = { provider: true }
provider.headers = { provider: "provider", "X-Shared": "provider" }
provider.body = {
provider: true,
reasoning: { type: "enabled", budget: 8_000 },
stop: ["END"],
disabled: { type: "enabled" },
}
})
catalog.model.update(providerID, modelID, (model) => {
model.headers = { model: "model", shared: "model" }
model.body = { model: true, request: true, shared: "model" }
model.headers = { model: "model", "x-shared": "model" }
model.body = {
model: true,
request: true,
reasoning: { budget: 32_000 },
stop: ["STOP"],
disabled: null,
}
})
})
const model = required(yield* catalog.model.get(providerID, modelID))
expect(model.headers).toEqual({ provider: "provider", shared: "model", model: "model" })
expect(model.body).toEqual({ provider: true, model: true, request: true, shared: "model" })
expect(model.headers).toEqual({ provider: "provider", "x-shared": "model", model: "model" })
expect(model.body).toEqual({
provider: true,
model: true,
request: true,
reasoning: { type: "enabled", budget: 32_000 },
stop: ["STOP"],
disabled: null,
})
}),
)

View file

@ -101,8 +101,7 @@ describe("Config", () => {
})
expect(migrated.providers?.bedrock).toMatchObject({
package: "@ai-sdk/amazon-bedrock",
aisdk: true,
package: "aisdk:@ai-sdk/amazon-bedrock",
settings: { baseURL: "https://bedrock.example.com", region: "us-east-1", profile: "dev" },
headers: { "x-test": "1" },
body: { trace: true },
@ -588,8 +587,7 @@ describe("Config", () => {
},
})
expect(documents[0]?.info.providers?.openai).toMatchObject({
package: "@ai-sdk/openai",
aisdk: true,
package: "aisdk:@ai-sdk/openai",
settings: { apiKey: "secret", organization: "org" },
models: {
model: {

View file

@ -61,8 +61,7 @@ describe("ConfigProviderPlugin.Plugin", () => {
info: decode({
providers: {
opencode: {
package: "@ai-sdk/openai",
aisdk: true,
package: "aisdk:@ai-sdk/openai",
settings: { baseURL: "https://opencode.test/v1" },
models: {
"alpha-gpt-next": {
@ -114,8 +113,7 @@ describe("ConfigProviderPlugin.Plugin", () => {
info: decode({
providers: {
opencode: {
package: "@ai-sdk/openai",
aisdk: true,
package: "aisdk:@ai-sdk/openai",
settings: { baseURL: "https://opencode.test/v1" },
},
},
@ -195,8 +193,7 @@ describe("ConfigProviderPlugin.Plugin", () => {
model: "custom/default",
providers: {
custom: {
package: "custom-sdk",
aisdk: true,
package: "aisdk:custom-sdk",
settings: { baseURL: "https://example.test" },
headers: { last: "last", shared: "last" },
models: {
@ -247,8 +244,7 @@ describe("ConfigProviderPlugin.Plugin", () => {
})
expect((yield* integrations.get(Integration.ID.make("custom")))?.name).toBe("Renamed")
expect(provider.disabled).toBeUndefined()
expect(provider.package).toBe("custom-sdk")
expect(provider.aisdk).toBe(true)
expect(provider.package).toBe("aisdk:custom-sdk")
expect(provider.settings).toEqual({ baseURL: "https://example.test" })
expect(provider.headers).toEqual({ first: "first", shared: "last", last: "last" })
expect(model.id).toBe(modelID)

View file

@ -29,8 +29,7 @@ describe("AlibabaPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("alibaba"), ModelV2.ID.make("qwen")),
modelID: ModelV2.ID.make("qwen"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/alibaba",
options: { name: "alibaba" },
@ -48,8 +47,7 @@ describe("AlibabaPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("alibaba"), ModelV2.ID.make("qwen")),
modelID: ModelV2.ID.make("qwen"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/openai-compatible",
options: { name: "alibaba" },
@ -67,8 +65,7 @@ describe("AlibabaPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-alibaba"), ModelV2.ID.make("qwen")),
modelID: ModelV2.ID.make("qwen"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/alibaba",
options: { name: "custom-alibaba", apiKey: "test" },
@ -88,8 +85,7 @@ describe("AlibabaPlugin", () => {
const item = ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("alibaba"), ModelV2.ID.make("alias")),
modelID: ModelV2.ID.make("qwen-plus"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
})
const result = yield* aisdk.runSDK({ model: item, package: "@ai-sdk/alibaba", options: {} })
const language = result.sdk?.languageModel(item.modelID ?? item.id)

View file

@ -85,13 +85,12 @@ describe("AmazonBedrockPlugin", () => {
yield* catalog.transform((catalog) => {
const bedrock = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.amazonBedrock),
aisdk: true,
package: "@ai-sdk/amazon-bedrock",
package: "aisdk:@ai-sdk/amazon-bedrock",
headers: {},
settings: { endpoint: "https://bedrock.example" },
})
catalog.provider.update(bedrock.id, (item) => {
item.aisdk = bedrock.aisdk
item.package = bedrock.package
item.package = bedrock.package
item.headers = bedrock.headers
item.settings = { endpoint: "https://bedrock.example" }
@ -100,8 +99,7 @@ describe("AmazonBedrockPlugin", () => {
yield* addPlugin()
const result = required(yield* catalog.provider.get(ProviderV2.ID.amazonBedrock))
expect(result).toMatchObject({
aisdk: true,
package: "@ai-sdk/amazon-bedrock",
package: "aisdk:@ai-sdk/amazon-bedrock",
settings: { baseURL: "https://bedrock.example" },
})
expect(result.settings?.endpoint).toBeUndefined()
@ -118,8 +116,7 @@ describe("AmazonBedrockPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/amazon-bedrock",
options: {
@ -145,8 +142,7 @@ describe("AmazonBedrockPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/amazon-bedrock",
options: {
@ -181,8 +177,7 @@ describe("AmazonBedrockPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/amazon-bedrock",
options: { name: "amazon-bedrock" },
@ -203,8 +198,7 @@ describe("AmazonBedrockPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/amazon-bedrock",
options: { name: "amazon-bedrock", region: "eu-west-1" },
@ -224,8 +218,7 @@ describe("AmazonBedrockPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/amazon-bedrock",
options: { name: "amazon-bedrock" },
@ -245,8 +238,7 @@ describe("AmazonBedrockPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/amazon-bedrock",
options: { name: "amazon-bedrock" },
@ -267,8 +259,7 @@ describe("AmazonBedrockPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/amazon-bedrock",
options: {
@ -298,8 +289,7 @@ describe("AmazonBedrockPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/amazon-bedrock",
options: {
@ -328,8 +318,7 @@ describe("AmazonBedrockPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("openai.gpt-5.5")),
modelID: ModelV2.ID.make("openai.gpt-5.5"),
aisdk: true,
package: "@ai-sdk/amazon-bedrock/mantle",
package: "aisdk:@ai-sdk/amazon-bedrock/mantle",
}),
package: "@ai-sdk/amazon-bedrock/mantle",
options: {
@ -357,8 +346,7 @@ describe("AmazonBedrockPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("openai.gpt-5.5")),
modelID: ModelV2.ID.make("openai.gpt-5.5"),
aisdk: true,
package: "@ai-sdk/amazon-bedrock/mantle",
package: "aisdk:@ai-sdk/amazon-bedrock/mantle",
}),
sdk: fakeSelectorSdk(calls),
options: { baseURL: "https://bedrock-mantle.us-east-2.api.aws/openai/v1", region: "us-east-2" },
@ -367,8 +355,7 @@ describe("AmazonBedrockPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("openai.gpt-oss-safeguard-120b")),
modelID: ModelV2.ID.make("openai.gpt-oss-safeguard-120b"),
aisdk: true,
package: "@ai-sdk/amazon-bedrock/mantle",
package: "aisdk:@ai-sdk/amazon-bedrock/mantle",
}),
sdk: fakeSelectorSdk(calls),
options: { region: "us-east-1" },
@ -386,8 +373,7 @@ describe("AmazonBedrockPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
aisdk: true,
package: "@ai-sdk/amazon-bedrock/anthropic",
package: "aisdk:@ai-sdk/amazon-bedrock/anthropic",
}),
package: "@ai-sdk/amazon-bedrock/anthropic",
options: { name: "amazon-bedrock" },
@ -415,8 +401,7 @@ describe("AmazonBedrockPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/amazon-bedrock",
options: {
@ -448,8 +433,7 @@ describe("AmazonBedrockPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
options: {},
@ -458,8 +442,7 @@ describe("AmazonBedrockPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
options: { region: "eu-west-1" },
@ -468,8 +451,7 @@ describe("AmazonBedrockPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("global.anthropic.claude-sonnet-4-5")),
modelID: ModelV2.ID.make("global.anthropic.claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
options: { region: "eu-west-1" },
@ -478,8 +460,7 @@ describe("AmazonBedrockPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
options: { region: "ap-northeast-1" },
@ -488,8 +469,7 @@ describe("AmazonBedrockPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
options: { region: "ap-southeast-2" },
@ -515,8 +495,7 @@ describe("AmazonBedrockPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
options: {},
@ -603,8 +582,7 @@ describe("AmazonBedrockPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make(item.modelID)),
modelID: ModelV2.ID.make(item.modelID),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
options: { region: item.region },
@ -624,8 +602,7 @@ describe("AmazonBedrockPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.openai, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
options: { region: "eu-west-1" },

View file

@ -31,13 +31,12 @@ describe("AnthropicPlugin", () => {
yield* catalog.transform((catalog) => {
const item = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.anthropic),
aisdk: true,
package: "@ai-sdk/anthropic",
package: "aisdk:@ai-sdk/anthropic",
headers: { Existing: "1" },
settings: {},
})
catalog.provider.update(item.id, (draft) => {
draft.aisdk = item.aisdk
draft.package = item.package
draft.package = item.package
draft.headers = item.headers
})
@ -68,8 +67,7 @@ describe("AnthropicPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-anthropic"), ModelV2.ID.make("claude-sonnet-4-5")),
modelID: ModelV2.ID.make("claude-sonnet-4-5"),
aisdk: true,
package: "@ai-sdk/anthropic",
package: "aisdk:@ai-sdk/anthropic",
}),
package: "@ai-sdk/anthropic",
options: { name: "custom-anthropic", apiKey: "test" },
@ -87,8 +85,7 @@ describe("AnthropicPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.anthropic, ModelV2.ID.make("claude-sonnet-4-5")),
modelID: ModelV2.ID.make("claude-sonnet-4-5"),
aisdk: true,
package: "@ai-sdk/anthropic",
package: "aisdk:@ai-sdk/anthropic",
}),
package: "@ai-sdk/anthropic",
options: { name: "anthropic", apiKey: "test" },

View file

@ -66,15 +66,13 @@ describe("AzureCognitiveServicesPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
catalog.provider.update(ProviderV2.ID.make("azure-cognitive-services"), (item) => {
item.aisdk = true
item.package = "@ai-sdk/openai-compatible"
item.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
})
})
yield* addPlugin()
const result = required(yield* catalog.provider.get(ProviderV2.ID.make("azure-cognitive-services")))
expect(result).toMatchObject({
aisdk: true,
package: "@ai-sdk/openai-compatible",
package: "aisdk:@ai-sdk/openai-compatible",
settings: { baseURL: "https://cognitive.cognitiveservices.azure.com/openai" },
})
expect(result.settings?.resourceName).toBeUndefined()
@ -89,20 +87,18 @@ describe("AzureCognitiveServicesPlugin", () => {
yield* catalog.transform((catalog) => {
const azure = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.make("azure-cognitive-services")),
aisdk: true,
package: "@ai-sdk/openai-compatible",
package: "aisdk:@ai-sdk/openai-compatible",
})
const openai = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.openai),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
})
catalog.provider.update(azure.id, (item) => {
item.aisdk = azure.aisdk
item.package = azure.package
item.package = azure.package
})
catalog.provider.update(openai.id, (item) => {
item.aisdk = openai.aisdk
item.package = openai.package
item.package = openai.package
})
})
@ -110,9 +106,9 @@ describe("AzureCognitiveServicesPlugin", () => {
const azure = required(yield* catalog.provider.get(ProviderV2.ID.make("azure-cognitive-services")))
const openai = required(yield* catalog.provider.get(ProviderV2.ID.openai))
expect(azure.settings?.baseURL).toBeUndefined()
expect(azure).toMatchObject({ aisdk: true, package: "@ai-sdk/openai-compatible" })
expect(azure).toMatchObject({ package: "aisdk:@ai-sdk/openai-compatible" })
expect(openai.settings?.baseURL).toBeUndefined()
expect(openai).toMatchObject({ aisdk: true, package: "test-provider" })
expect(openai).toMatchObject({ package: "aisdk:test-provider" })
}),
),
)
@ -127,8 +123,7 @@ describe("AzureCognitiveServicesPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("azure-cognitive-services"), ModelV2.ID.make("deployment")),
modelID: ModelV2.ID.make("deployment"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: fakeSelectorSdk(calls),
options: { useCompletionUrls: true },
@ -147,8 +142,7 @@ describe("AzureCognitiveServicesPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("azure-cognitive-services"), ModelV2.ID.make("deployment")),
modelID: ModelV2.ID.make("deployment"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: fakeSelectorSdk(calls),
options: {},
@ -157,8 +151,7 @@ describe("AzureCognitiveServicesPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.openai, ModelV2.ID.make("deployment")),
modelID: ModelV2.ID.make("deployment"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: fakeSelectorSdk(calls),
options: {},
@ -179,8 +172,7 @@ describe("AzureCognitiveServicesPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("azure-cognitive-services"), ModelV2.ID.make("messages-deployment")),
modelID: ModelV2.ID.make("messages-deployment"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: { messages: sdk.messages, chat: sdk.chat, languageModel: sdk.languageModel },
options: {},
@ -189,8 +181,7 @@ describe("AzureCognitiveServicesPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("azure-cognitive-services"), ModelV2.ID.make("chat-deployment")),
modelID: ModelV2.ID.make("chat-deployment"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: { chat: sdk.chat, languageModel: sdk.languageModel },
options: {},
@ -199,8 +190,7 @@ describe("AzureCognitiveServicesPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("azure-cognitive-services"), ModelV2.ID.make("language-deployment")),
modelID: ModelV2.ID.make("language-deployment"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: { languageModel: sdk.languageModel },
options: {},

View file

@ -66,8 +66,7 @@ describe("AzurePlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
catalog.provider.update(ProviderV2.ID.azure, (item) => {
item.aisdk = true
item.package = "@ai-sdk/azure"
item.package = ProviderV2.aisdk("@ai-sdk/azure")
})
})
yield* addPlugin()
@ -83,13 +82,12 @@ describe("AzurePlugin", () => {
yield* catalog.transform((catalog) => {
const azure = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.azure),
aisdk: true,
package: "@ai-sdk/azure",
package: "aisdk:@ai-sdk/azure",
headers: {},
settings: { resourceName: "from-config" },
})
catalog.provider.update(azure.id, (item) => {
item.aisdk = azure.aisdk
item.package = azure.package
item.package = azure.package
item.headers = azure.headers
item.settings = { resourceName: "from-config" }
@ -110,13 +108,12 @@ describe("AzurePlugin", () => {
yield* catalog.transform((catalog) => {
const azure = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.azure),
aisdk: true,
package: "@ai-sdk/azure",
package: "aisdk:@ai-sdk/azure",
headers: {},
settings: { resourceName: "" },
})
catalog.provider.update(azure.id, (item) => {
item.aisdk = azure.aisdk
item.package = azure.package
item.package = azure.package
item.headers = azure.headers
item.settings = { resourceName: "" }
@ -135,13 +132,12 @@ describe("AzurePlugin", () => {
yield* catalog.transform((catalog) => {
const azure = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.azure),
aisdk: true,
package: "@ai-sdk/azure",
package: "aisdk:@ai-sdk/azure",
headers: {},
settings: { resourceName: " " },
})
catalog.provider.update(azure.id, (item) => {
item.aisdk = azure.aisdk
item.package = azure.package
item.package = azure.package
item.headers = azure.headers
item.settings = { resourceName: " " }
@ -163,8 +159,7 @@ describe("AzurePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("deployment")),
modelID: ModelV2.ID.make("deployment"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/azure",
options: { name: "azure", baseURL: "https://proxy.example.com/openai" },
@ -184,8 +179,7 @@ describe("AzurePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("deployment")),
modelID: ModelV2.ID.make("deployment"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/azure",
options: { name: "azure" },
@ -206,8 +200,7 @@ describe("AzurePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("deployment")),
modelID: ModelV2.ID.make("deployment"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: fakeSelectorSdk(calls),
options: { useCompletionUrls: true },
@ -226,8 +219,7 @@ describe("AzurePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("deployment")),
modelID: ModelV2.ID.make("deployment"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: fakeSelectorSdk(calls),
options: { useCompletionUrls: true },
@ -246,8 +238,7 @@ describe("AzurePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("deployment")),
modelID: ModelV2.ID.make("deployment"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
headers: {},
settings: { useCompletionUrls: true },
}),
@ -268,8 +259,7 @@ describe("AzurePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("deployment")),
modelID: ModelV2.ID.make("deployment"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: fakeSelectorSdk(calls),
options: {},
@ -278,8 +268,7 @@ describe("AzurePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.openai, ModelV2.ID.make("deployment")),
modelID: ModelV2.ID.make("deployment"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: fakeSelectorSdk(calls),
options: {},
@ -303,8 +292,7 @@ describe("AzurePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("messages-deployment")),
modelID: ModelV2.ID.make("messages-deployment"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: { messages: make("messages"), chat: make("chat"), languageModel: make("languageModel") },
options: {},
@ -313,8 +301,7 @@ describe("AzurePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("language-deployment")),
modelID: ModelV2.ID.make("language-deployment"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: { languageModel: make("languageModel") },
options: {},

View file

@ -36,8 +36,7 @@ describe("CerebrasPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
catalog.provider.update(ProviderV2.ID.make("cerebras"), (item) => {
item.aisdk = true
item.package = "@ai-sdk/cerebras"
item.package = ProviderV2.aisdk("@ai-sdk/cerebras")
item.headers = { ...item.headers, Existing: "1" }
})
})
@ -71,8 +70,7 @@ describe("CerebrasPlugin", () => {
ModelV2.ID.make("llama-4-scout-17b-16e-instruct"),
),
modelID: ModelV2.ID.make("llama-4-scout-17b-16e-instruct"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/cerebras",
options: { name: "custom-cerebras", apiKey: "test" },
@ -95,8 +93,7 @@ describe("CerebrasPlugin", () => {
ModelV2.ID.make("llama-4-scout-17b-16e-instruct"),
),
modelID: ModelV2.ID.make("llama-4-scout-17b-16e-instruct"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/cerebras",
options: { name: "configured-cerebras", apiKey: "test" },
@ -118,8 +115,7 @@ describe("CerebrasPlugin", () => {
ModelV2.ID.make("llama-4-scout-17b-16e-instruct"),
),
modelID: ModelV2.ID.make("llama-4-scout-17b-16e-instruct"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/groq",
options: { name: "custom-cerebras", apiKey: "test" },

View file

@ -119,8 +119,7 @@ describe("CloudflareAIGatewayPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
modelID: ModelV2.ID.make("openai/gpt-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "ai-gateway-provider",
options: { name: "cloudflare-ai-gateway" },
@ -142,8 +141,7 @@ describe("CloudflareAIGatewayPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
modelID: ModelV2.ID.make("openai/gpt-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "ai-gateway-provider",
options: {
@ -188,8 +186,7 @@ describe("CloudflareAIGatewayPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
modelID: ModelV2.ID.make("openai/gpt-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "ai-gateway-provider",
options: {
@ -219,8 +216,7 @@ describe("CloudflareAIGatewayPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
modelID: ModelV2.ID.make("openai/gpt-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "ai-gateway-provider",
options: {
@ -258,8 +254,7 @@ describe("CloudflareAIGatewayPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
modelID: ModelV2.ID.make("openai/gpt-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "ai-gateway-provider",
options: {
@ -291,8 +286,7 @@ describe("CloudflareAIGatewayPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
modelID: ModelV2.ID.make("openai/gpt-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "ai-gateway-provider",
options: { name: "cloudflare-ai-gateway" },
@ -315,8 +309,7 @@ describe("CloudflareAIGatewayPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
modelID: ModelV2.ID.make("openai/gpt-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "ai-gateway-provider",
options: { name: "cloudflare-ai-gateway" },
@ -340,8 +333,7 @@ describe("CloudflareAIGatewayPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
modelID: ModelV2.ID.make("openai/gpt-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "ai-gateway-provider",
options: { name: "cloudflare-ai-gateway" },
@ -371,8 +363,7 @@ describe("CloudflareAIGatewayPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
modelID: ModelV2.ID.make("openai/gpt-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "ai-gateway-provider",
options: { name: "cloudflare-ai-gateway", baseURL: "https://proxy.example/v1" },
@ -399,8 +390,7 @@ describe("CloudflareAIGatewayPlugin", () => {
ModelV2.ID.make("anthropic/claude-sonnet-4-5"),
),
modelID: ModelV2.ID.make("anthropic/claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "ai-gateway-provider",
options: { name: "cloudflare-ai-gateway" },
@ -429,8 +419,7 @@ describe("CloudflareAIGatewayPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
modelID: ModelV2.ID.make("openai/gpt-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/openai-compatible",
options: { name: "cloudflare-ai-gateway" },

View file

@ -87,8 +87,7 @@ describe("CloudflareWorkersAIPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) =>
catalog.provider.update(ProviderV2.ID.make("cloudflare-workers-ai"), (provider) => {
provider.aisdk = true
provider.package = "test-provider"
provider.package = ProviderV2.aisdk("test-provider")
}),
)
yield* addPlugin()
@ -97,7 +96,6 @@ describe("CloudflareWorkersAIPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("@cf/model")),
modelID: ModelV2.ID.make("@cf/model"),
aisdk: provider.aisdk,
package: provider.package,
settings: provider.settings,
}),
@ -105,8 +103,7 @@ describe("CloudflareWorkersAIPlugin", () => {
options: { name: "cloudflare-workers-ai", headers: { custom: "header" } },
})
expect(provider).toMatchObject({
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
settings: { baseURL: "https://api.cloudflare.com/client/v4/accounts/acct/ai/v1" },
})
expect(sdk.sdk).toBeDefined()
@ -120,15 +117,13 @@ describe("CloudflareWorkersAIPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) =>
catalog.provider.update(ProviderV2.ID.make("cloudflare-workers-ai"), (provider) => {
provider.aisdk = true
provider.package = "test-provider"
provider.package = ProviderV2.aisdk("test-provider")
provider.settings = { ...provider.settings, baseURL: "https://proxy.example/v1" }
}),
)
yield* addPlugin()
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("cloudflare-workers-ai")))).toMatchObject({
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
settings: { baseURL: "https://proxy.example/v1" },
})
}),
@ -145,8 +140,7 @@ describe("CloudflareWorkersAIPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("@cf/model")),
modelID: ModelV2.ID.make("@cf/model"),
aisdk: true,
package: "@ai-sdk/openai-compatible",
package: "aisdk:@ai-sdk/openai-compatible",
settings: { baseURL: "https://proxy.example/v1" },
}),
package: "@ai-sdk/openai-compatible",
@ -163,15 +157,13 @@ describe("CloudflareWorkersAIPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) =>
catalog.provider.update(ProviderV2.ID.make("cloudflare-workers-ai"), (provider) => {
provider.aisdk = true
provider.package = "test-provider"
provider.package = ProviderV2.aisdk("test-provider")
provider.settings = { ...provider.settings, accountId: "configured-acct" }
}),
)
yield* addPlugin()
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("cloudflare-workers-ai")))).toMatchObject({
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
settings: { baseURL: "https://api.cloudflare.com/client/v4/accounts/env-acct/ai/v1" },
})
}),
@ -188,8 +180,7 @@ describe("CloudflareWorkersAIPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("@cf/model")),
modelID: ModelV2.ID.make("@cf/model"),
aisdk: true,
package: "@ai-sdk/openai-compatible",
package: "aisdk:@ai-sdk/openai-compatible",
settings: { baseURL: "https://proxy.example/v1" },
}),
package: "@ai-sdk/openai-compatible",
@ -218,8 +209,7 @@ describe("CloudflareWorkersAIPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("@cf/model")),
modelID: ModelV2.ID.make("@cf/model"),
aisdk: true,
package: "@ai-sdk/openai-compatible",
package: "aisdk:@ai-sdk/openai-compatible",
settings: { baseURL: "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/ai/v1" },
}),
package: "@ai-sdk/openai-compatible",
@ -245,8 +235,7 @@ describe("CloudflareWorkersAIPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("alias")),
modelID: ModelV2.ID.make("@cf/api-model"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: fakeSelectorSdk(calls),
options: {},
@ -266,8 +255,7 @@ describe("CloudflareWorkersAIPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("@cf/model")),
modelID: ModelV2.ID.make("@cf/model"),
aisdk: true,
package: "@ai-sdk/anthropic",
package: "aisdk:@ai-sdk/anthropic",
settings: { baseURL: "https://proxy.example/v1" },
}),
package: "@ai-sdk/anthropic",

View file

@ -57,8 +57,7 @@ describe("CoherePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cohere"), ModelV2.ID.make("command")),
modelID: ModelV2.ID.make("command"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/openai-compatible",
options: { name: "cohere" },
@ -69,8 +68,7 @@ describe("CoherePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cohere"), ModelV2.ID.make("command")),
modelID: ModelV2.ID.make("command"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/cohere",
options: { name: "cohere" },
@ -88,8 +86,7 @@ describe("CoherePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-cohere"), ModelV2.ID.make("command-r-plus")),
modelID: ModelV2.ID.make("command-r-plus"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/cohere",
options: { name: "custom-cohere", apiKey: "test", baseURL: "https://cohere.example" },
@ -115,8 +112,7 @@ describe("CoherePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cohere"), ModelV2.ID.make("alias")),
modelID: ModelV2.ID.make("command-r-plus"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk,
options: {},

View file

@ -49,8 +49,7 @@ describe("DeepInfraPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("deepinfra"), ModelV2.ID.make("model")),
modelID: ModelV2.ID.make("model"),
aisdk: true,
package: "@ai-sdk/deepinfra",
package: "aisdk:@ai-sdk/deepinfra",
}),
package: "@ai-sdk/deepinfra",
options: { name: "deepinfra" },
@ -69,8 +68,7 @@ describe("DeepInfraPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-deepinfra"), ModelV2.ID.make("model")),
modelID: ModelV2.ID.make("model"),
aisdk: true,
package: "@ai-sdk/deepinfra",
package: "aisdk:@ai-sdk/deepinfra",
}),
package: "@ai-sdk/deepinfra",
options: { name: "custom-deepinfra", apiKey: "test" },
@ -90,8 +88,7 @@ describe("DeepInfraPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("deepinfra"), ModelV2.ID.make("model")),
modelID: ModelV2.ID.make("model"),
aisdk: true,
package: "@ai-sdk/deepinfra",
package: "aisdk:@ai-sdk/deepinfra",
}),
package: "@ai-sdk/deepinfra",
options: { name: "deepinfra", apiKey: "test" },
@ -118,8 +115,7 @@ describe("DeepInfraPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("deepinfra"), ModelV2.ID.make("model")),
modelID: ModelV2.ID.make("model"),
aisdk: true,
package: "@ai-sdk/deepinfra",
package: "aisdk:@ai-sdk/deepinfra",
}),
package: item,
options: { name: "deepinfra" },
@ -131,8 +127,7 @@ describe("DeepInfraPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("deepinfra"), ModelV2.ID.make("model")),
modelID: ModelV2.ID.make("model"),
aisdk: true,
package: "@ai-sdk/deepinfra",
package: "aisdk:@ai-sdk/deepinfra",
}),
package: "@ai-sdk/deepinfra",
options: { name: "deepinfra" },
@ -152,8 +147,7 @@ describe("DeepInfraPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("deepinfra"), ModelV2.ID.make("meta-llama/Llama-3.3-70B-Instruct")),
modelID: ModelV2.ID.make("meta-llama/Llama-3.3-70B-Instruct"),
aisdk: true,
package: "@ai-sdk/deepinfra",
package: "aisdk:@ai-sdk/deepinfra",
}),
package: "@ai-sdk/deepinfra",
options: { name: "deepinfra" },

View file

@ -54,8 +54,7 @@ describe("DynamicProviderPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("test-model")),
modelID: ModelV2.ID.make("test-model"),
aisdk: true,
package: fixtureProvider,
package: ProviderV2.aisdk(fixtureProvider),
}),
package: fixtureProvider,
options: { name: "custom", marker: "dynamic" },
@ -74,8 +73,7 @@ describe("DynamicProviderPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("test-model")),
modelID: ModelV2.ID.make("test-model"),
aisdk: true,
package: fixtureProvider,
package: ProviderV2.aisdk(fixtureProvider),
}),
package: fixtureProvider,
options: { name: "custom", marker: "dynamic" },
@ -93,8 +91,7 @@ describe("DynamicProviderPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-provider"), ModelV2.ID.make("test-model")),
modelID: ModelV2.ID.make("test-model"),
aisdk: true,
package: fixtureProvider,
package: ProviderV2.aisdk(fixtureProvider),
}),
package: fixtureProvider,
options: { name: "custom-provider", marker: "dynamic" },
@ -111,8 +108,7 @@ describe("DynamicProviderPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("npm-provider"), ModelV2.ID.make("test-model")),
modelID: ModelV2.ID.make("test-model"),
aisdk: true,
package: "fixture-provider",
package: "aisdk:fixture-provider",
}),
package: "fixture-provider",
options: { name: "npm-provider", marker: "npm" },
@ -130,8 +126,7 @@ describe("DynamicProviderPlugin", () => {
ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("missing-entrypoint"), ModelV2.ID.make("alias")),
modelID: ModelV2.ID.make("alias"),
aisdk: true,
package: "fixture-provider",
package: "aisdk:fixture-provider",
}),
)
.pipe(Effect.exit)
@ -149,8 +144,7 @@ describe("DynamicProviderPlugin", () => {
ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("bad-import"), ModelV2.ID.make("alias")),
modelID: ModelV2.ID.make("alias"),
aisdk: true,
package: "file:///missing/provider-factory.js",
package: "aisdk:file:///missing/provider-factory.js",
}),
)
.pipe(Effect.exit)
@ -170,8 +164,7 @@ describe("DynamicProviderPlugin", () => {
ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("missing-factory"), ModelV2.ID.make("alias")),
modelID: ModelV2.ID.make("alias"),
aisdk: true,
package: "fixture-provider",
package: "aisdk:fixture-provider",
}),
)
.pipe(Effect.exit)
@ -189,8 +182,7 @@ describe("DynamicProviderPlugin", () => {
ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("alias")),
modelID: ModelV2.ID.make("test-model-api"),
aisdk: true,
package: fixtureProvider,
package: ProviderV2.aisdk(fixtureProvider),
}),
)
expect(language).toMatchObject({ modelID: "test-model-api", options: { name: "custom" } })

View file

@ -46,8 +46,7 @@ describe("GatewayPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("gateway"), ModelV2.ID.make("model")),
modelID: ModelV2.ID.make("model"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/gateway",
options: { name: "gateway" },
@ -68,8 +67,7 @@ describe("GatewayPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("vercel"), ModelV2.ID.make("anthropic/claude-sonnet-4")),
modelID: ModelV2.ID.make("anthropic/claude-sonnet-4"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/gateway",
options: { name: "vercel", apiKey: "test-key" },
@ -92,8 +90,7 @@ describe("GatewayPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("vercel"), ModelV2.ID.make(modelID)),
modelID: ModelV2.ID.make(modelID),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/vercel",
options: { name: "vercel" },
@ -104,8 +101,7 @@ describe("GatewayPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("vercel"), ModelV2.ID.make(modelID)),
modelID: ModelV2.ID.make(modelID),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/gateway",
options: { name: "vercel" },

View file

@ -48,8 +48,7 @@ describe("GithubCopilotPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-5")),
modelID: ModelV2.ID.make("gpt-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/openai-compatible",
options: { name: "github-copilot" },
@ -58,8 +57,7 @@ describe("GithubCopilotPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-5")),
modelID: ModelV2.ID.make("gpt-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/github-copilot",
options: { name: "github-copilot" },
@ -79,8 +77,7 @@ describe("GithubCopilotPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("claude-sonnet-4")),
modelID: ModelV2.ID.make("claude-sonnet-4"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
options: {},
@ -99,8 +96,7 @@ describe("GithubCopilotPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("alias")),
modelID: ModelV2.ID.make("claude-sonnet-4"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
options: {},
@ -119,8 +115,7 @@ describe("GithubCopilotPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-5")),
modelID: ModelV2.ID.make("gpt-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: fakeSelectorSdk(calls),
options: {},
@ -129,8 +124,7 @@ describe("GithubCopilotPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-5.1-codex")),
modelID: ModelV2.ID.make("gpt-5.1-codex"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: fakeSelectorSdk(calls),
options: {},
@ -139,8 +133,7 @@ describe("GithubCopilotPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-4o")),
modelID: ModelV2.ID.make("gpt-4o"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: fakeSelectorSdk(calls),
options: {},
@ -149,8 +142,7 @@ describe("GithubCopilotPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-5-mini")),
modelID: ModelV2.ID.make("gpt-5-mini"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: fakeSelectorSdk(calls),
options: {},
@ -159,8 +151,7 @@ describe("GithubCopilotPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-5-mini-2025-08-07")),
modelID: ModelV2.ID.make("gpt-5-mini-2025-08-07"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: fakeSelectorSdk(calls),
options: {},
@ -185,8 +176,7 @@ describe("GithubCopilotPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("default")),
modelID: ModelV2.ID.make("gpt-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: fakeSelectorSdk(calls),
options: {},
@ -195,8 +185,7 @@ describe("GithubCopilotPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("small")),
modelID: ModelV2.ID.make("gpt-5-mini"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: fakeSelectorSdk(calls),
options: {},
@ -205,8 +194,7 @@ describe("GithubCopilotPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("sonnet")),
modelID: ModelV2.ID.make("claude-sonnet-4"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: fakeSelectorSdk(calls),
options: {},
@ -255,8 +243,7 @@ describe("GithubCopilotPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("openai"), ModelV2.ID.make("gpt-5")),
modelID: ModelV2.ID.make("gpt-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: fakeSelectorSdk(calls),
options: {},

View file

@ -71,8 +71,7 @@ describe("GitLabPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("claude")),
modelID: ModelV2.ID.make("claude"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "gitlab-ai-provider",
options: { name: "gitlab" },
@ -110,8 +109,7 @@ describe("GitLabPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("claude")),
modelID: ModelV2.ID.make("claude"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "gitlab-ai-provider",
options: { name: "gitlab" },
@ -137,8 +135,7 @@ describe("GitLabPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("claude")),
modelID: ModelV2.ID.make("claude"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "gitlab-ai-provider",
options: {
@ -180,8 +177,7 @@ describe("GitLabPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("claude")),
modelID: ModelV2.ID.make("claude"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/openai",
options: { name: "gitlab" },
@ -201,8 +197,7 @@ describe("GitLabPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("duo-workflow-custom")),
modelID: ModelV2.ID.make("duo-workflow-custom"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
headers: {},
settings: { workflowRef: "ref", workflowDefinition: "definition" },
}),
@ -236,8 +231,7 @@ describe("GitLabPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("duo-workflow-exact")),
modelID: ModelV2.ID.make("duo-workflow-exact"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: {
workflowChat: (id: string, options: unknown) => {
@ -265,8 +259,7 @@ describe("GitLabPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("duo-workflow-custom")),
modelID: ModelV2.ID.make("duo-workflow-custom"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
headers: {},
settings: { featureFlags: { request_flag: true } },
}),
@ -293,8 +286,7 @@ describe("GitLabPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("claude")),
modelID: ModelV2.ID.make("claude"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
headers: { h: "v" },
settings: {},
}),

View file

@ -64,8 +64,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) =>
catalog.provider.update(ProviderV2.ID.make("google-vertex-anthropic"), (provider) => {
provider.aisdk = true
provider.package = "@ai-sdk/google-vertex/anthropic"
provider.package = ProviderV2.aisdk("@ai-sdk/google-vertex/anthropic")
}),
)
yield* addPlugin(GoogleVertexAnthropicPlugin)
@ -85,8 +84,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) =>
catalog.provider.update(ProviderV2.ID.make("google-vertex-anthropic"), (provider) => {
provider.aisdk = true
provider.package = "@ai-sdk/google-vertex/anthropic"
provider.package = ProviderV2.aisdk("@ai-sdk/google-vertex/anthropic")
provider.settings = { ...provider.settings, project: "configured-project", location: "configured-location" }
}),
)
@ -123,8 +121,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
ModelV2.ID.make("claude-sonnet-4-5"),
),
modelID: ModelV2.ID.make("claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/google-vertex/anthropic",
options: { name: "google-vertex-anthropic" },
@ -151,8 +148,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
ModelV2.ID.make("claude-sonnet-4-5"),
),
modelID: ModelV2.ID.make("claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/google-vertex/anthropic",
options: { name: "google-vertex-anthropic" },
@ -173,8 +169,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("claude-sonnet-4-5")),
modelID: ModelV2.ID.make("claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/google-vertex/anthropic",
options: { name: "google-vertex", project: "project", location: "eu" },
@ -194,8 +189,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("claude-sonnet-4-5")),
modelID: ModelV2.ID.make("claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/google-vertex/anthropic",
options: { name: "google-vertex", project: "project", location: "eu", baseURL: "https://proxy.example/v1" },
@ -214,8 +208,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make(" claude-sonnet-4-5 ")),
modelID: ModelV2.ID.make(" claude-sonnet-4-5 "),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/google-vertex/anthropic",
options: { name: "google-vertex", project: "project", location: "us" },
@ -224,8 +217,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make(" claude-sonnet-4-5 ")),
modelID: ModelV2.ID.make(" claude-sonnet-4-5 "),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: sdkResult.sdk,
options: {},
@ -248,8 +240,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex-anthropic"), ModelV2.ID.make(" claude-sonnet-4-5 ")),
modelID: ModelV2.ID.make(" claude-sonnet-4-5 "),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: { languageModel: selector(calls) },
options: {},
@ -268,8 +259,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("claude-sonnet-4-5")),
modelID: ModelV2.ID.make("claude-sonnet-4-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: { languageModel: selector(calls) },
options: {},

View file

@ -92,8 +92,7 @@ describe("GoogleVertexPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) =>
catalog.provider.update(ProviderV2.ID.opencode, (provider) => {
provider.aisdk = true
provider.package = "@ai-sdk/openai-compatible"
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
provider.settings = { ...provider.settings, baseURL: "https://opencode.ai/zen/v1" }
}),
)
@ -119,8 +118,7 @@ describe("GoogleVertexPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) =>
catalog.provider.update(ProviderV2.ID.make("google-vertex"), (provider) => {
provider.aisdk = true
provider.package = "@ai-sdk/openai-compatible"
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
provider.settings = {
...provider.settings,
baseURL:
@ -133,8 +131,7 @@ describe("GoogleVertexPlugin", () => {
expect(provider.settings?.project).toBe("google-cloud-project")
expect(provider.settings?.location).toBe("google-vertex-location")
expect(provider).toMatchObject({
aisdk: true,
package: "@ai-sdk/openai-compatible",
package: "aisdk:@ai-sdk/openai-compatible",
settings: {
baseURL:
"https://google-vertex-location-aiplatform.googleapis.com/v1/projects/google-cloud-project/locations/google-vertex-location",
@ -163,8 +160,7 @@ describe("GoogleVertexPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) =>
catalog.provider.update(ProviderV2.ID.make("google-vertex"), (provider) => {
provider.aisdk = true
provider.package = "@ai-sdk/openai-compatible"
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
provider.settings = {
...provider.settings,
baseURL:
@ -178,8 +174,7 @@ describe("GoogleVertexPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("gemini")),
modelID: ModelV2.ID.make("gemini"),
aisdk: true,
package: "@ai-sdk/google-vertex",
package: "aisdk:@ai-sdk/google-vertex",
}),
package: "@ai-sdk/google-vertex",
options: { name: "google-vertex" },
@ -187,8 +182,7 @@ describe("GoogleVertexPlugin", () => {
expect(provider.settings?.project).toBe("vertex-project")
expect(provider).toMatchObject({
aisdk: true,
package: "@ai-sdk/openai-compatible",
package: "aisdk:@ai-sdk/openai-compatible",
settings: {
baseURL:
"https://europe-west4-aiplatform.googleapis.com/v1/projects/vertex-project/locations/europe-west4",
@ -215,8 +209,7 @@ describe("GoogleVertexPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) =>
catalog.provider.update(ProviderV2.ID.make("google-vertex"), (provider) => {
provider.aisdk = true
provider.package = "@ai-sdk/openai-compatible"
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
provider.settings = {
...provider.settings,
baseURL:
@ -230,8 +223,7 @@ describe("GoogleVertexPlugin", () => {
expect(provider.settings?.project).toBe("config-project")
expect(provider.settings?.location).toBe("global")
expect(provider).toMatchObject({
aisdk: true,
package: "@ai-sdk/openai-compatible",
package: "aisdk:@ai-sdk/openai-compatible",
settings: { baseURL: "https://aiplatform.googleapis.com/v1/projects/config-project/locations/global" },
})
}),
@ -243,8 +235,7 @@ describe("GoogleVertexPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) =>
catalog.provider.update(ProviderV2.ID.make("google-vertex"), (provider) => {
provider.aisdk = true
provider.package = "@ai-sdk/openai-compatible"
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
provider.settings = {
...provider.settings,
baseURL:
@ -256,8 +247,7 @@ describe("GoogleVertexPlugin", () => {
yield* addPlugin()
const provider = required(yield* catalog.provider.get(ProviderV2.ID.make("google-vertex")))
expect(provider).toMatchObject({
aisdk: true,
package: "@ai-sdk/openai-compatible",
package: "aisdk:@ai-sdk/openai-compatible",
settings: { baseURL: "https://eu-aiplatform.googleapis.com/v1/projects/config-project/locations/eu" },
})
}),
@ -278,8 +268,7 @@ describe("GoogleVertexPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) =>
catalog.provider.update(ProviderV2.ID.make("google-vertex"), (provider) => {
provider.aisdk = true
provider.package = "@ai-sdk/google-vertex"
provider.package = ProviderV2.aisdk("@ai-sdk/google-vertex")
provider.settings = { ...provider.settings, project: "config-project" }
}),
)
@ -307,8 +296,7 @@ describe("GoogleVertexPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("gemini")),
modelID: ModelV2.ID.make("gemini"),
aisdk: true,
package: "@ai-sdk/google-vertex",
package: "aisdk:@ai-sdk/google-vertex",
}),
package: "@ai-sdk/google-vertex",
options: { name: "google-vertex" },
@ -353,8 +341,7 @@ describe("GoogleVertexPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("gemini")),
modelID: ModelV2.ID.make("gemini"),
aisdk: true,
package: "@ai-sdk/openai-compatible",
package: "aisdk:@ai-sdk/openai-compatible",
}),
package: "@ai-sdk/openai-compatible",
options: { name: "google-vertex" },
@ -382,8 +369,7 @@ describe("GoogleVertexPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make(" gemini-2.5-pro ")),
modelID: ModelV2.ID.make(" gemini-2.5-pro "),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
options: {},

View file

@ -28,8 +28,7 @@ describe("GooglePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-google"), ModelV2.ID.make("gemini")),
modelID: ModelV2.ID.make("gemini"),
aisdk: true,
package: "@ai-sdk/google",
package: "aisdk:@ai-sdk/google",
}),
package: "@ai-sdk/google",
options: { name: "custom-google", apiKey: "test" },
@ -48,8 +47,7 @@ describe("GooglePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google"), ModelV2.ID.make("gemini")),
modelID: ModelV2.ID.make("gemini"),
aisdk: true,
package: "@ai-sdk/google",
package: "aisdk:@ai-sdk/google",
}),
package: "@ai-sdk/google-vertex",
options: { name: "google" },
@ -67,8 +65,7 @@ describe("GooglePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-google"), ModelV2.ID.make("alias")),
modelID: ModelV2.ID.make("gemini-api"),
aisdk: true,
package: "@ai-sdk/google",
package: "aisdk:@ai-sdk/google",
}),
package: "@ai-sdk/google",
options: { name: "custom-google", apiKey: "test" },

View file

@ -29,8 +29,7 @@ describe("GroqPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("groq"), ModelV2.ID.make("llama")),
modelID: ModelV2.ID.make("llama"),
aisdk: true,
package: "@ai-sdk/groq",
package: "aisdk:@ai-sdk/groq",
}),
package: "@ai-sdk/groq",
options: { name: "groq" },
@ -48,8 +47,7 @@ describe("GroqPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("groq"), ModelV2.ID.make("llama")),
modelID: ModelV2.ID.make("llama"),
aisdk: true,
package: "@ai-sdk/groq",
package: "aisdk:@ai-sdk/groq",
}),
package: "@ai-sdk/openai-compatible",
options: { name: "groq" },
@ -67,8 +65,7 @@ describe("GroqPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("groq"), ModelV2.ID.make("llama")),
modelID: ModelV2.ID.make("llama"),
aisdk: true,
package: "@ai-sdk/groq",
package: "aisdk:@ai-sdk/groq",
}),
package: "@ai-sdk/groq/compat",
options: { name: "groq" },
@ -86,8 +83,7 @@ describe("GroqPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-groq"), ModelV2.ID.make("llama")),
modelID: ModelV2.ID.make("llama"),
aisdk: true,
package: "@ai-sdk/groq",
package: "aisdk:@ai-sdk/groq",
}),
package: "@ai-sdk/groq",
options: { name: "custom-groq", apiKey: "test" },
@ -113,8 +109,7 @@ describe("GroqPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("groq"), ModelV2.ID.make("alias")),
modelID: ModelV2.ID.make("llama-api"),
aisdk: true,
package: "@ai-sdk/groq",
package: "aisdk:@ai-sdk/groq",
}),
sdk,
options: { name: "groq", apiKey: "test" },

View file

@ -27,8 +27,7 @@ describe("KiloPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
catalog.provider.update(ProviderV2.ID.make("kilo"), (provider) => {
provider.aisdk = true
provider.package = "@ai-sdk/openai-compatible"
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
provider.settings = { ...provider.settings, baseURL: "https://api.kilo.ai/api/gateway" }
provider.headers = { Existing: "value" }
})
@ -49,8 +48,7 @@ describe("KiloPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
catalog.provider.update(ProviderV2.ID.make("kilo"), (provider) => {
provider.aisdk = true
provider.package = "@ai-sdk/openai-compatible"
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
provider.settings = { ...provider.settings, baseURL: "https://api.kilo.ai/api/gateway" }
})
})
@ -71,13 +69,11 @@ describe("KiloPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
catalog.provider.update(ProviderV2.ID.make("kilo"), (provider) => {
provider.aisdk = true
provider.package = "@ai-sdk/openai-compatible"
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
provider.settings = { ...provider.settings, baseURL: "https://api.kilo.ai/api/gateway" }
})
catalog.provider.update(ProviderV2.ID.make("custom-kilo"), (provider) => {
provider.aisdk = true
provider.package = "kilo"
provider.package = ProviderV2.aisdk("kilo")
})
})
yield* addPlugin()

View file

@ -34,8 +34,7 @@ describe("LLMGatewayPlugin", () => {
})
yield* catalog.transform((catalog) => {
catalog.provider.update(ProviderV2.ID.make("llmgateway"), (provider) => {
provider.aisdk = true
provider.package = "@ai-sdk/openai-compatible"
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
provider.settings = { ...provider.settings, baseURL: "https://api.llmgateway.io/v1" }
provider.headers = { Existing: "value" }
})
@ -62,8 +61,7 @@ describe("LLMGatewayPlugin", () => {
yield* catalog.transform((catalog) => {
catalog.provider.update(ProviderV2.ID.make("llmgateway"), (provider) => {
provider.disabled = true
provider.aisdk = true
provider.package = "@ai-sdk/openai-compatible"
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
provider.settings = { ...provider.settings, baseURL: "https://api.llmgateway.io/v1" }
})
})

View file

@ -29,8 +29,7 @@ describe("MistralPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("mistral"), ModelV2.ID.make("mistral-large")),
modelID: ModelV2.ID.make("mistral-large"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/mistral",
options: { name: "mistral" },
@ -48,8 +47,7 @@ describe("MistralPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("mistral"), ModelV2.ID.make("mistral-large")),
modelID: ModelV2.ID.make("mistral-large"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/openai-compatible",
options: { name: "mistral" },
@ -73,8 +71,7 @@ describe("MistralPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("mistral"), ModelV2.ID.make("mistral-large")),
modelID: ModelV2.ID.make("mistral-large"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/mistral",
options: { name: "mistral" },
@ -99,8 +96,7 @@ describe("MistralPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-mistral"), ModelV2.ID.make("mistral-large")),
modelID: ModelV2.ID.make("mistral-large"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/mistral",
options: { name: "custom-mistral" },
@ -125,8 +121,7 @@ describe("MistralPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("mistral"), ModelV2.ID.make("alias")),
modelID: ModelV2.ID.make("mistral-large"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk,
options: {},

View file

@ -27,8 +27,7 @@ describe("NvidiaPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
catalog.provider.update(ProviderV2.ID.make("nvidia"), (provider) => {
provider.aisdk = true
provider.package = "@ai-sdk/openai-compatible"
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
provider.settings = { ...provider.settings, baseURL: "https://integrate.api.nvidia.com/v1" }
provider.headers = { Existing: "value" }
})
@ -50,8 +49,7 @@ describe("NvidiaPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
catalog.provider.update(ProviderV2.ID.make("nvidia"), (provider) => {
provider.aisdk = true
provider.package = "@ai-sdk/openai-compatible"
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
provider.settings = { ...provider.settings, baseURL: "https://integrate.api.nvidia.com/v1" }
})
})
@ -70,8 +68,7 @@ describe("NvidiaPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
catalog.provider.update(ProviderV2.ID.make("nvidia"), (provider) => {
provider.aisdk = true
provider.package = "@ai-sdk/openai-compatible"
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
provider.settings = { ...provider.settings, baseURL: "https://integrate.api.nvidia.com/v1" }
provider.headers = { "X-BILLING-INVOKE-ORIGIN": "CustomOrigin" }
})

View file

@ -28,8 +28,7 @@ describe("OpenAICompatiblePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("model")),
modelID: ModelV2.ID.make("model"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/openai-compatible",
options: { name: "custom" },
@ -38,8 +37,7 @@ describe("OpenAICompatiblePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("model")),
modelID: ModelV2.ID.make("model"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/openai-compatible",
options: { name: "custom", includeUsage: false },
@ -58,8 +56,7 @@ describe("OpenAICompatiblePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("model")),
modelID: ModelV2.ID.make("model"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "file:///tmp/@ai-sdk/openai-compatible-provider.js",
options: { name: "custom" },
@ -83,8 +80,7 @@ describe("OpenAICompatiblePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-provider"), ModelV2.ID.make("model")),
modelID: ModelV2.ID.make("model"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/openai-compatible",
options: { name: "custom-provider", baseURL: "https://example.com/v1" },
@ -105,8 +101,7 @@ describe("OpenAICompatiblePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("model")),
modelID: ModelV2.ID.make("model"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/openai-compatible",
options: { name: "cloudflare-workers-ai" },

View file

@ -68,8 +68,7 @@ describe("OpenAIPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-openai"), ModelV2.ID.make("gpt-5")),
modelID: ModelV2.ID.make("gpt-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/openai",
options: { name: "custom-openai", apiKey: "test" },
@ -87,8 +86,7 @@ describe("OpenAIPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.openai, ModelV2.ID.make("gpt-5")),
modelID: ModelV2.ID.make("gpt-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/openai-compatible",
options: { name: "openai" },
@ -107,8 +105,7 @@ describe("OpenAIPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.openai, ModelV2.ID.make("alias")),
modelID: ModelV2.ID.make("gpt-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: fakeSelectorSdk(calls),
options: {},
@ -128,8 +125,7 @@ describe("OpenAIPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.anthropic, ModelV2.ID.make("gpt-5")),
modelID: ModelV2.ID.make("gpt-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: fakeSelectorSdk(calls),
options: {},
@ -145,11 +141,10 @@ describe("OpenAIPlugin", () => {
yield* catalog.transform((catalog) => {
const item = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.openai),
aisdk: true,
package: "@ai-sdk/openai",
package: "aisdk:@ai-sdk/openai",
})
catalog.provider.update(item.id, (draft) => {
draft.aisdk = item.aisdk
draft.package = item.package
draft.package = item.package
})
catalog.model.update(item.id, ModelV2.ID.make("gpt-5"), () => {})
@ -169,11 +164,10 @@ describe("OpenAIPlugin", () => {
yield* catalog.transform((catalog) => {
const item = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.make("custom-openai")),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
})
catalog.provider.update(item.id, (draft) => {
draft.aisdk = item.aisdk
draft.package = item.package
draft.package = item.package
})
catalog.model.update(item.id, ModelV2.ID.make("gpt-5-chat-latest"), () => {})

View file

@ -133,8 +133,7 @@ describe("OpencodePlugin", () => {
expect(provider).toMatchObject({
name: "Remote",
integrationID: "opencode",
aisdk: true,
package: "@ai-sdk/openai-compatible",
package: "aisdk:@ai-sdk/openai-compatible",
settings: { baseURL: `${server.url.origin}/v1` },
})
expect(provider.headers).toEqual({ "x-org-id": "org" })
@ -178,14 +177,12 @@ describe("OpencodePlugin", () => {
yield* catalog.transform((catalog) => {
const provider = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
})
const model = ModelV2.Info.make({
...ModelV2.Info.empty(provider.id, ModelV2.ID.make("paid")),
modelID: ModelV2.ID.make("paid"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
cost: cost(1),
})
catalog.provider.update(provider.id, () => {})
@ -207,14 +204,12 @@ describe("OpencodePlugin", () => {
yield* catalog.transform((catalog) => {
const provider = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
})
const model = ModelV2.Info.make({
...ModelV2.Info.empty(provider.id, ModelV2.ID.make("free")),
modelID: ModelV2.ID.make("free"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
cost: cost(0),
})
catalog.provider.update(provider.id, () => {})
@ -236,14 +231,12 @@ describe("OpencodePlugin", () => {
yield* catalog.transform((catalog) => {
const provider = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
})
const model = ModelV2.Info.make({
...ModelV2.Info.empty(provider.id, ModelV2.ID.make("output-only")),
modelID: ModelV2.ID.make("output-only"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
cost: cost(0, 1),
})
catalog.provider.update(provider.id, () => {})
@ -267,14 +260,12 @@ describe("OpencodePlugin", () => {
yield* catalog.transform((catalog) => {
const provider = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
})
const model = ModelV2.Info.make({
...ModelV2.Info.empty(provider.id, ModelV2.ID.make("paid")),
modelID: ModelV2.ID.make("paid"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
cost: cost(1),
})
catalog.provider.update(provider.id, () => {})
@ -303,14 +294,12 @@ describe("OpencodePlugin", () => {
yield* catalog.transform((catalog) => {
const provider = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
})
const model = ModelV2.Info.make({
...ModelV2.Info.empty(provider.id, ModelV2.ID.make("paid")),
modelID: ModelV2.ID.make("paid"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
cost: cost(1),
})
catalog.provider.update(provider.id, () => {})
@ -332,16 +321,14 @@ describe("OpencodePlugin", () => {
yield* catalog.transform((catalog) => {
const provider = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
headers: {},
settings: { apiKey: "configured" },
})
const model = ModelV2.Info.make({
...ModelV2.Info.empty(provider.id, ModelV2.ID.make("paid")),
modelID: ModelV2.ID.make("paid"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
cost: cost(1),
})
catalog.provider.update(provider.id, (draft) => {
@ -366,14 +353,12 @@ describe("OpencodePlugin", () => {
yield* catalog.transform((catalog) => {
const provider = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.openai),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
})
const model = ModelV2.Info.make({
...ModelV2.Info.empty(provider.id, ModelV2.ID.make("paid")),
modelID: ModelV2.ID.make("paid"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
cost: cost(1),
})
catalog.provider.update(provider.id, () => {})

View file

@ -30,8 +30,7 @@ describe("OpenRouterPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
catalog.provider.update(ProviderV2.ID.openrouter, (provider) => {
provider.aisdk = true
provider.package = "@openrouter/ai-sdk-provider"
provider.package = ProviderV2.aisdk("@openrouter/ai-sdk-provider")
provider.headers = { Existing: "value" }
provider.settings = {}
})
@ -58,8 +57,7 @@ describe("OpenRouterPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.openrouter, ModelV2.ID.make("openai/gpt-5")),
modelID: ModelV2.ID.make("openai/gpt-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/openai-compatible",
options: { name: "openrouter" },
@ -70,8 +68,7 @@ describe("OpenRouterPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("openai/gpt-5")),
modelID: ModelV2.ID.make("openai/gpt-5"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@openrouter/ai-sdk-provider",
options: { name: "custom" },
@ -85,8 +82,7 @@ describe("OpenRouterPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
catalog.provider.update(ProviderV2.ID.openrouter, (provider) => {
provider.aisdk = true
provider.package = "@openrouter/ai-sdk-provider"
provider.package = ProviderV2.aisdk("@openrouter/ai-sdk-provider")
})
catalog.provider.update(ProviderV2.ID.openai, () => {})
catalog.model.update(ProviderV2.ID.openrouter, ModelV2.ID.make("openai/gpt-5-chat"), () => {})

View file

@ -42,8 +42,7 @@ describe("PerplexityPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("perplexity"), ModelV2.ID.make("sonar")),
modelID: ModelV2.ID.make("sonar"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/perplexity",
options: { name: "perplexity" },
@ -61,8 +60,7 @@ describe("PerplexityPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("perplexity"), ModelV2.ID.make("sonar")),
modelID: ModelV2.ID.make("sonar"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/perplexity-compatible",
options: { name: "perplexity" },
@ -80,8 +78,7 @@ describe("PerplexityPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("perplexity"), ModelV2.ID.make("sonar")),
modelID: ModelV2.ID.make("sonar"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/perplexity",
options: { name: "perplexity" },
@ -99,8 +96,7 @@ describe("PerplexityPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-perplexity"), ModelV2.ID.make("sonar")),
modelID: ModelV2.ID.make("sonar"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/perplexity",
options: { name: "custom-perplexity" },
@ -119,8 +115,7 @@ describe("PerplexityPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("perplexity"), ModelV2.ID.make("alias")),
modelID: ModelV2.ID.make("sonar"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: fakeSelectorSdk(calls),
options: {},

View file

@ -50,8 +50,7 @@ function model(providerID: string) {
return ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make(providerID), ModelV2.ID.make("sap-model")),
modelID: ModelV2.ID.make("sap-model"),
aisdk: true,
package: fixtureProvider,
package: ProviderV2.aisdk(fixtureProvider),
})
}

View file

@ -58,8 +58,7 @@ describe("SnowflakeCortexPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("openai"), ModelV2.ID.make("gpt-4")),
modelID: ModelV2.ID.make("gpt-4"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/openai",
options: { name: "openai" },
@ -78,8 +77,7 @@ describe("SnowflakeCortexPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("snowflake-cortex"), ModelV2.ID.make("claude-sonnet-4-6")),
modelID: ModelV2.ID.make("claude-sonnet-4-6"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/openai-compatible",
options: { name: "snowflake-cortex", baseURL: "https://test.snowflakecomputing.com/api/v2/cortex/v1" },
@ -99,8 +97,7 @@ describe("SnowflakeCortexPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("snowflake-cortex"), ModelV2.ID.make("claude-sonnet-4-6")),
modelID: ModelV2.ID.make("claude-sonnet-4-6"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/openai-compatible",
options: {
@ -124,8 +121,7 @@ describe("SnowflakeCortexPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("snowflake-cortex"), ModelV2.ID.make("claude-sonnet-4-6")),
modelID: ModelV2.ID.make("claude-sonnet-4-6"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/openai-compatible",
options: { name: "snowflake-cortex", baseURL: "https://test.snowflakecomputing.com/api/v2/cortex/v1" },
@ -145,8 +141,7 @@ describe("SnowflakeCortexPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("snowflake-cortex"), ModelV2.ID.make("claude-sonnet-4-6")),
modelID: ModelV2.ID.make("claude-sonnet-4-6"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/openai-compatible",
options: {
@ -170,8 +165,7 @@ describe("SnowflakeCortexPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("snowflake-cortex"), ModelV2.ID.make("claude-sonnet-4-6")),
modelID: ModelV2.ID.make("claude-sonnet-4-6"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/openai-compatible",
options: { name: "snowflake-cortex", baseURL: "https://test.snowflakecomputing.com/api/v2/cortex/v1" },

View file

@ -42,8 +42,7 @@ describe("TogetherAIPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("togetherai"), ModelV2.ID.make("model")),
modelID: ModelV2.ID.make("model"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/togetherai",
options: { name: "togetherai" },
@ -62,8 +61,7 @@ describe("TogetherAIPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("togetherai"), ModelV2.ID.make("model")),
modelID: ModelV2.ID.make("model"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "file:///tmp/@ai-sdk/togetherai-provider.js",
options: { name: "togetherai" },
@ -74,8 +72,7 @@ describe("TogetherAIPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("togetherai"), ModelV2.ID.make("model")),
modelID: ModelV2.ID.make("model"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/togetherai",
options: { name: "togetherai" },
@ -94,8 +91,7 @@ describe("TogetherAIPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-togetherai"), ModelV2.ID.make("model")),
modelID: ModelV2.ID.make("model"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/togetherai",
options: { name: "custom-togetherai" },
@ -119,8 +115,7 @@ describe("TogetherAIPlugin", () => {
ModelV2.ID.make("meta-llama/Llama-3.3-70B-Instruct-Turbo"),
),
modelID: ModelV2.ID.make("meta-llama/Llama-3.3-70B-Instruct-Turbo"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
options: {},

View file

@ -42,8 +42,7 @@ describe("VenicePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("venice"), ModelV2.ID.make("model")),
modelID: ModelV2.ID.make("model"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "venice-ai-sdk-provider",
options: { name: "venice" },
@ -61,8 +60,7 @@ describe("VenicePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-venice"), ModelV2.ID.make("model")),
modelID: ModelV2.ID.make("model"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "venice-ai-sdk-provider",
options: { name: "custom-venice", apiKey: "test" },
@ -81,8 +79,7 @@ describe("VenicePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("venice"), ModelV2.ID.make("model")),
modelID: ModelV2.ID.make("model"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "file:///tmp/venice-ai-sdk-provider.js",
options: { name: "venice" },
@ -91,8 +88,7 @@ describe("VenicePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("venice"), ModelV2.ID.make("model")),
modelID: ModelV2.ID.make("model"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
package: "@ai-sdk/openai-compatible",
options: { name: "venice" },
@ -112,8 +108,7 @@ describe("VenicePlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("venice"), ModelV2.ID.make("alias")),
modelID: ModelV2.ID.make("alias"),
aisdk: true,
package: "test-provider",
package: "aisdk:test-provider",
}),
sdk: fakeSelectorSdk(calls),
options: {},

View file

@ -25,8 +25,7 @@ describe("VercelPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
catalog.provider.update(ProviderV2.ID.make("vercel"), (provider) => {
provider.aisdk = true
provider.package = "@ai-sdk/vercel"
provider.package = ProviderV2.aisdk("@ai-sdk/vercel")
provider.headers = { ...provider.headers, Existing: "1" }
})
})
@ -44,8 +43,7 @@ describe("VercelPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) =>
catalog.provider.update(ProviderV2.ID.make("vercel"), (provider) => {
provider.aisdk = true
provider.package = "@ai-sdk/vercel"
provider.package = ProviderV2.aisdk("@ai-sdk/vercel")
}),
)
yield* addPlugin()
@ -63,8 +61,7 @@ describe("VercelPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-vercel"), ModelV2.ID.make("v0-1.0-md")),
modelID: ModelV2.ID.make("v0-1.0-md"),
aisdk: true,
package: "@ai-sdk/vercel",
package: "aisdk:@ai-sdk/vercel",
}),
package: "@ai-sdk/vercel",
options: { name: "custom-vercel" },

View file

@ -43,8 +43,7 @@ describe("XAIPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("xai"), ModelV2.ID.make("grok-4")),
modelID: ModelV2.ID.make("grok-4"),
aisdk: true,
package: "@ai-sdk/xai",
package: "aisdk:@ai-sdk/xai",
}),
package: "@ai-sdk/openai-compatible",
options: {},
@ -54,8 +53,7 @@ describe("XAIPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("xai"), ModelV2.ID.make("grok-4")),
modelID: ModelV2.ID.make("grok-4"),
aisdk: true,
package: "@ai-sdk/xai",
package: "aisdk:@ai-sdk/xai",
}),
package: "@ai-sdk/xai",
options: {},
@ -76,8 +74,7 @@ describe("XAIPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-xai"), ModelV2.ID.make("grok-4")),
modelID: ModelV2.ID.make("grok-4"),
aisdk: true,
package: "@ai-sdk/xai",
package: "aisdk:@ai-sdk/xai",
}),
package: "@ai-sdk/xai",
options: {},
@ -98,8 +95,7 @@ describe("XAIPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("xai"), ModelV2.ID.make("alias")),
modelID: ModelV2.ID.make("grok-4"),
aisdk: true,
package: "@ai-sdk/xai",
package: "aisdk:@ai-sdk/xai",
}),
sdk: fakeSelectorSdk(calls),
options: {},
@ -121,8 +117,7 @@ describe("XAIPlugin", () => {
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.openai, ModelV2.ID.make("grok-4")),
modelID: ModelV2.ID.make("grok-4"),
aisdk: true,
package: "@ai-sdk/xai",
package: "aisdk:@ai-sdk/xai",
}),
sdk: fakeSelectorSdk(calls),
options: {},

View file

@ -32,8 +32,7 @@ describe("ZenmuxPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
catalog.provider.update(ProviderV2.ID.make("zenmux"), (provider) => {
provider.aisdk = true
provider.package = "@ai-sdk/openai-compatible"
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
provider.settings = { ...provider.settings, baseURL: "https://zenmux.ai/api/v1" }
})
})
@ -49,8 +48,7 @@ describe("ZenmuxPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
catalog.provider.update(ProviderV2.ID.make("zenmux"), (provider) => {
provider.aisdk = true
provider.package = "@ai-sdk/openai-compatible"
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
provider.settings = { ...provider.settings, baseURL: "https://zenmux.ai/api/v1" }
provider.headers = { ...provider.headers, Existing: "value" }
})
@ -70,8 +68,7 @@ describe("ZenmuxPlugin", () => {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
catalog.provider.update(ProviderV2.ID.make("zenmux"), (provider) => {
provider.aisdk = true
provider.package = "@ai-sdk/openai-compatible"
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
provider.settings = { ...provider.settings, baseURL: "https://zenmux.ai/api/v1" }
provider.headers = { "HTTP-Referer": "https://example.com/", "X-Title": "custom-title" }
})

View file

@ -23,13 +23,7 @@ const connections = Credential.defaultLayer.pipe(Layer.fresh)
const integrations = Integration.locationLayer.pipe(Layer.provide(events), Layer.provide(connections))
const catalog = Catalog.layer.pipe(
Layer.provide(
Layer.mergeAll(
events,
locationLayer,
Policy.layer.pipe(Layer.provide(locationLayer)),
connections,
integrations,
),
Layer.mergeAll(events, locationLayer, Policy.layer.pipe(Layer.provide(locationLayer)), connections, integrations),
),
)
const it = testEffect(
@ -42,13 +36,11 @@ describe("VariantPlugin", () => {
const service = yield* Catalog.Service
yield* service.transform((catalog) => {
catalog.provider.update(ProviderV2.ID.opencode, (provider) => {
provider.package = "@ai-sdk/openai-compatible"
provider.aisdk = true
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
})
catalog.model.update(ProviderV2.ID.opencode, ModelV2.ID.make("glm-5.2"), (model) => {
model.modelID = ModelV2.ID.make("glm-5.2")
model.package = "@ai-sdk/openai-compatible"
model.aisdk = true
model.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
})
})
yield* VariantPlugin.Plugin.effect(host({ catalog: catalogHost(service) }))
@ -66,11 +58,8 @@ describe("VariantPlugin", () => {
yield* service.transform((catalog) => {
catalog.model.update(ProviderV2.ID.opencode, ModelV2.ID.make("glm-5.2"), (model) => {
model.modelID = ModelV2.ID.make("glm-5.2")
model.package = "@ai-sdk/openai-compatible"
model.aisdk = true
model.variants = [
{ id: ModelV2.VariantID.make("high"), headers: { custom: "true" } },
]
model.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
model.variants = [{ id: ModelV2.VariantID.make("high"), headers: { custom: "true" } }]
})
})
yield* VariantPlugin.Plugin.effect(host({ catalog: catalogHost(service) }))

View file

@ -14,7 +14,6 @@ import { it } from "./lib/effect"
type Transport = {
readonly package: string
readonly aisdk?: true
readonly settings?: Record<string, unknown>
}
@ -38,10 +37,36 @@ const model = (transport: Transport, variants?: NonNullable<ModelV2.Info["varian
})
describe("SessionRunnerModel", () => {
it.effect("constructs native provider package models mechanically", () =>
Effect.gen(function* () {
const resolved = yield* SessionRunnerModel.fromCatalogModel(
model({
package: "@opencode-ai/llm/providers/openai",
settings: { baseURL: "https://openai.example/v1" },
}),
Credential.Key.make({ type: "key", key: "runtime-secret" }),
)
const headers = yield* resolved.route.auth.apply({
request: LLM.request({ model: resolved, prompt: "Hello" }),
method: "POST",
url: "https://openai.example/v1/responses",
body: "{}",
headers: Headers.empty,
})
expect(String(resolved.id)).toBe("api-test-model")
expect(resolved.route.id).toBe("openai-responses")
expect(resolved.route.defaults.headers).toEqual({ "x-test": "header" })
expect(resolved.route.defaults.http?.body).toEqual({ custom_extension: { enabled: true } })
expect(resolved.route.defaults.limits).toEqual({ context: 100, output: 20 })
expect(headers.authorization).toBe("Bearer runtime-secret")
}),
)
it.effect("maps catalog OpenAI AI SDK models into native Responses routes", () =>
Effect.gen(function* () {
const resolved = yield* SessionRunnerModel.fromCatalogModel(
model({ package: "@ai-sdk/openai", aisdk: true, settings: { baseURL: "https://openai.example/v1" } }),
model({ package: "aisdk:@ai-sdk/openai", settings: { baseURL: "https://openai.example/v1" } }),
)
expect(resolved).toMatchObject({ id: "api-test-model", provider: "test-provider" })
@ -60,7 +85,7 @@ describe("SessionRunnerModel", () => {
it.effect("keeps catalog apiKey credentials out of provider JSON", () =>
Effect.gen(function* () {
const resolved = yield* SessionRunnerModel.fromCatalogModel(
model({ package: "@ai-sdk/openai", aisdk: true, settings: { baseURL: "https://openai.example/v1" } }),
model({ package: "aisdk:@ai-sdk/openai", settings: { baseURL: "https://openai.example/v1" } }),
)
const prepared = yield* LLMClient.prepare(LLM.request({ model: resolved, prompt: "Hello" }))
@ -74,8 +99,7 @@ describe("SessionRunnerModel", () => {
const resolved = yield* SessionRunnerModel.fromCatalogModel(
ModelV2.Info.make({
...model({
package: "@ai-sdk/openai-compatible",
aisdk: true,
package: "aisdk:@ai-sdk/openai-compatible",
settings: {
apiKey: "settings-secret",
baseURL: "https://compatible.example/v1",
@ -102,7 +126,7 @@ describe("SessionRunnerModel", () => {
it.effect("overlays selected OpenAI Session variant bodies", () =>
Effect.gen(function* () {
const catalog = model({ package: "@ai-sdk/openai", aisdk: true, settings: { baseURL: "https://openai.example/v1" } }, [
const catalog = model({ package: "aisdk:@ai-sdk/openai", settings: { baseURL: "https://openai.example/v1" } }, [
{
id: ModelV2.VariantID.make("high"),
headers: { "x-variant": "high" },
@ -146,8 +170,7 @@ describe("SessionRunnerModel", () => {
Effect.gen(function* () {
const catalog = model(
{
package: "@ai-sdk/openai-compatible",
aisdk: true,
package: "aisdk:@ai-sdk/openai-compatible",
settings: { baseURL: "https://compatible.example/v1" },
},
[
@ -181,7 +204,7 @@ describe("SessionRunnerModel", () => {
it.effect("rejects an explicit unavailable Session variant during model resolution", () =>
Effect.gen(function* () {
const catalog = model({ package: "@ai-sdk/openai", aisdk: true, settings: { baseURL: "https://openai.example/v1" } })
const catalog = model({ package: "aisdk:@ai-sdk/openai", settings: { baseURL: "https://openai.example/v1" } })
const session = SessionV2.Info.make({
id: SessionV2.ID.make("ses_model_variant_unavailable"),
projectID: ProjectV2.ID.global,
@ -211,13 +234,16 @@ describe("SessionRunnerModel", () => {
it.effect("overlays selected Anthropic Session variant bodies", () =>
Effect.gen(function* () {
const catalog = model({ package: "@ai-sdk/anthropic", aisdk: true, settings: { baseURL: "https://anthropic.example/v1" } }, [
{
id: ModelV2.VariantID.make("high"),
headers: {},
body: { thinking: { type: "enabled", budget_tokens: 12000 } },
},
])
const catalog = model(
{ package: "aisdk:@ai-sdk/anthropic", settings: { baseURL: "https://anthropic.example/v1" } },
[
{
id: ModelV2.VariantID.make("high"),
headers: {},
body: { thinking: { type: "enabled", budget_tokens: 12000 } },
},
],
)
const session = SessionV2.Info.make({
id: SessionV2.ID.make("ses_anthropic_variant"),
projectID: ProjectV2.ID.global,
@ -241,7 +267,7 @@ describe("SessionRunnerModel", () => {
it.effect("maps catalog Anthropic AI SDK models into native routes", () =>
Effect.gen(function* () {
const resolved = yield* SessionRunnerModel.fromCatalogModel(
model({ package: "@ai-sdk/anthropic", aisdk: true, settings: { baseURL: "https://anthropic.example/v1" } }),
model({ package: "aisdk:@ai-sdk/anthropic", settings: { baseURL: "https://anthropic.example/v1" } }),
)
expect(resolved.route).toMatchObject({
@ -255,7 +281,7 @@ describe("SessionRunnerModel", () => {
Effect.gen(function* () {
const resolved = yield* SessionRunnerModel.fromCatalogModel(
ModelV2.Info.make({
...model({ package: "@ai-sdk/openai", aisdk: true, settings: { baseURL: "https://openai.example/v1" } }),
...model({ package: "aisdk:@ai-sdk/openai", settings: { baseURL: "https://openai.example/v1" } }),
headers: {},
body: {},
}),
@ -279,7 +305,7 @@ describe("SessionRunnerModel", () => {
const credential = Credential.Key.make({ type: "key", key: "stored-secret", metadata: { tenant: "work" } })
const resolved = yield* SessionRunnerModel.fromCatalogModel(
ModelV2.Info.make({
...model({ package: "@ai-sdk/openai", aisdk: true, settings: { baseURL: "https://openai.example/v1" } }),
...model({ package: "aisdk:@ai-sdk/openai", settings: { baseURL: "https://openai.example/v1" } }),
settings: { apiKey: "configured-secret", baseURL: "https://openai.example/v1" },
headers: {},
body: {},
@ -302,7 +328,7 @@ describe("SessionRunnerModel", () => {
it.effect("rejects catalog APIs without a native route", () =>
Effect.gen(function* () {
const failure = yield* SessionRunnerModel.fromCatalogModel(
model({ package: "@ai-sdk/google", aisdk: true, settings: { baseURL: "https://google.example/v1" } }),
model({ package: "aisdk:@ai-sdk/google", settings: { baseURL: "https://google.example/v1" } }),
).pipe(Effect.flip)
expect(failure).toMatchObject({
@ -319,15 +345,15 @@ describe("SessionRunnerModel", () => {
Effect.sync(() => {
expect(
SessionRunnerModel.supported(
model({ package: "@ai-sdk/openai", aisdk: true, settings: { baseURL: "https://openai.example/v1" } }),
model({ package: "aisdk:@ai-sdk/openai", settings: { baseURL: "https://openai.example/v1" } }),
),
).toBe(true)
expect(
SessionRunnerModel.supported(
model({ package: "@ai-sdk/google", aisdk: true, settings: { baseURL: "https://google.example/v1" } }),
model({ package: "aisdk:@ai-sdk/google", settings: { baseURL: "https://google.example/v1" } }),
),
).toBe(false)
expect(SessionRunnerModel.supported(model({ package: "native" }))).toBe(false)
expect(SessionRunnerModel.supported(model({ package: "native-provider-package" }))).toBe(true)
}),
)
})

View file

@ -3991,7 +3991,6 @@ export type ModelV2Info = {
family?: string
name: string
package?: string
aisdk?: true
settings?: {
[key: string]: unknown
}
@ -4048,7 +4047,6 @@ export type ProviderV2Info = {
name: string
disabled?: boolean
package: string
aisdk?: true
settings?: {
[key: string]: unknown
}