feat: add native provider packages
This commit is contained in:
parent
b10d617c80
commit
3b63568da2
88 changed files with 1946 additions and 1751 deletions
|
|
@ -3,8 +3,6 @@ import { Effect, Fiber, Layer, Stream } from "effect"
|
|||
import { Catalog } from "@opencode-ai/core/catalog"
|
||||
import { Integration } from "@opencode-ai/core/integration"
|
||||
import { Credential } from "@opencode-ai/core/credential"
|
||||
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
||||
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
||||
import { EventV2 } from "@opencode-ai/core/event"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
|
|
@ -23,11 +21,13 @@ const locationLayer = Layer.succeed(
|
|||
Location.Service,
|
||||
Location.Service.of(location({ directory: AbsolutePath.make("test") })),
|
||||
)
|
||||
const catalogLayer = AppNodeBuilder.build(
|
||||
LayerNode.group([Catalog.node, EventV2.node, Credential.node, Integration.node, Policy.node]),
|
||||
[[Location.node, locationLayer]],
|
||||
const it = testEffect(
|
||||
Catalog.locationLayer.pipe(
|
||||
Layer.provideMerge(EventV2.defaultLayer),
|
||||
Layer.provideMerge(locationLayer),
|
||||
Layer.provideMerge(Credential.defaultLayer),
|
||||
),
|
||||
)
|
||||
const it = testEffect(catalogLayer)
|
||||
|
||||
describe("CatalogV2", () => {
|
||||
it.effect("publishes an updated event after catalog changes", () =>
|
||||
|
|
@ -47,8 +47,11 @@ describe("CatalogV2", () => {
|
|||
|
||||
it.effect("derives availability from active credentials without changing provider state", () => {
|
||||
const integrationID = Integration.ID.make("test")
|
||||
const localCatalogLayer = Layer.fresh(
|
||||
AppNodeBuilder.build(LayerNode.group([Catalog.node, Credential.node]), [[Location.node, locationLayer]]),
|
||||
const layer = Catalog.locationLayer.pipe(
|
||||
Layer.fresh,
|
||||
Layer.provideMerge(EventV2.defaultLayer),
|
||||
Layer.provideMerge(locationLayer),
|
||||
Layer.provideMerge(Credential.defaultLayer.pipe(Layer.fresh)),
|
||||
)
|
||||
|
||||
return Effect.gen(function* () {
|
||||
|
|
@ -62,24 +65,25 @@ describe("CatalogV2", () => {
|
|||
})
|
||||
|
||||
expect((yield* catalog.provider.available()).map((provider) => provider.id)).toEqual([ProviderV2.ID.make("test")])
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("test"))).request.body).toEqual({})
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("test"))).body).toBeUndefined()
|
||||
yield* credentials.create({
|
||||
integrationID,
|
||||
label: "Second",
|
||||
value: Credential.Key.make({ type: "key", key: "second", metadata: { tenant: "two" } }),
|
||||
})
|
||||
expect((yield* catalog.provider.available()).map((provider) => provider.id)).toEqual([ProviderV2.ID.make("test")])
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("test"))).request.body).toEqual({})
|
||||
}).pipe(Effect.provide(localCatalogLayer))
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("test"))).body).toBeUndefined()
|
||||
}).pipe(Effect.provide(layer))
|
||||
})
|
||||
|
||||
it.effect("derives availability from a provider's integration", () => {
|
||||
const integrationID = Integration.ID.make("gateway")
|
||||
const providerID = ProviderV2.ID.make("remote")
|
||||
const localCatalogLayer = Layer.fresh(
|
||||
AppNodeBuilder.build(LayerNode.group([Catalog.node, Credential.node, Integration.node]), [
|
||||
[Location.node, locationLayer],
|
||||
]),
|
||||
const layer = Catalog.locationLayer.pipe(
|
||||
Layer.fresh,
|
||||
Layer.provideMerge(EventV2.defaultLayer),
|
||||
Layer.provideMerge(locationLayer),
|
||||
Layer.provideMerge(Credential.defaultLayer.pipe(Layer.fresh)),
|
||||
)
|
||||
|
||||
return Effect.gen(function* () {
|
||||
|
|
@ -98,7 +102,7 @@ describe("CatalogV2", () => {
|
|||
})
|
||||
|
||||
expect((yield* catalog.provider.available()).map((provider) => provider.id)).toEqual([providerID])
|
||||
}).pipe(Effect.provide(localCatalogLayer))
|
||||
}).pipe(Effect.provide(layer))
|
||||
})
|
||||
|
||||
it.effect("projects environment connections without a catalog plugin", () =>
|
||||
|
|
@ -131,111 +135,107 @@ describe("CatalogV2", () => {
|
|||
),
|
||||
)
|
||||
|
||||
it.effect("normalizes provider baseURL into api url", () =>
|
||||
it.effect("keeps provider API settings flat", () =>
|
||||
Effect.gen(function* () {
|
||||
const catalog = yield* Catalog.Service
|
||||
const providerID = ProviderV2.ID.make("test")
|
||||
yield* catalog.transform((catalog) =>
|
||||
catalog.provider.update(providerID, (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://default.example.com",
|
||||
}
|
||||
provider.request.body.baseURL = "https://override.example.com"
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
|
||||
provider.settings = { baseURL: "https://override.example.com" }
|
||||
}),
|
||||
)
|
||||
|
||||
expect(required(yield* catalog.provider.get(providerID)).api).toEqual({
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://override.example.com",
|
||||
expect(required(yield* catalog.provider.get(providerID))).toMatchObject({
|
||||
package: "aisdk:@ai-sdk/openai-compatible",
|
||||
settings: { baseURL: "https://override.example.com" },
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("normalizes model baseURL into api url", () =>
|
||||
it.effect("resolves model settings and upstream model ID", () =>
|
||||
Effect.gen(function* () {
|
||||
const catalog = yield* Catalog.Service
|
||||
const providerID = ProviderV2.ID.make("test")
|
||||
const modelID = ModelV2.ID.make("model")
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(providerID, (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://provider.example.com",
|
||||
}
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
|
||||
provider.settings = { baseURL: "https://provider.example.com" }
|
||||
})
|
||||
catalog.model.update(providerID, modelID, (model) => {
|
||||
model.api = {
|
||||
id: modelID,
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://model.example.com",
|
||||
}
|
||||
model.request.body.baseURL = "https://override.example.com"
|
||||
model.modelID = ModelV2.ID.make("upstream-model")
|
||||
model.settings = { baseURL: "https://override.example.com" }
|
||||
})
|
||||
})
|
||||
|
||||
expect(required(yield* catalog.model.get(providerID, modelID)).api).toEqual({
|
||||
expect(required(yield* catalog.model.get(providerID, modelID))).toMatchObject({
|
||||
id: modelID,
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://override.example.com",
|
||||
settings: {},
|
||||
modelID: ModelV2.ID.make("upstream-model"),
|
||||
package: "aisdk:@ai-sdk/openai-compatible",
|
||||
settings: { baseURL: "https://override.example.com" },
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("resolves default model api from provider api", () =>
|
||||
it.effect("resolves default model transport from provider transport", () =>
|
||||
Effect.gen(function* () {
|
||||
const catalog = yield* Catalog.Service
|
||||
const providerID = ProviderV2.ID.make("test")
|
||||
const modelID = ModelV2.ID.make("model")
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(providerID, (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://provider.example.com",
|
||||
}
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
|
||||
provider.settings = { baseURL: "https://provider.example.com" }
|
||||
})
|
||||
catalog.model.update(providerID, modelID, () => {})
|
||||
})
|
||||
|
||||
expect(required(yield* catalog.model.get(providerID, modelID)).api).toEqual({
|
||||
expect(required(yield* catalog.model.get(providerID, modelID))).toMatchObject({
|
||||
id: modelID,
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://provider.example.com",
|
||||
package: "aisdk:@ai-sdk/openai-compatible",
|
||||
settings: { baseURL: "https://provider.example.com" },
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("resolves provider and model request merges", () =>
|
||||
it.effect("resolves provider and model overlay merges", () =>
|
||||
Effect.gen(function* () {
|
||||
const catalog = yield* Catalog.Service
|
||||
const providerID = ProviderV2.ID.make("test")
|
||||
const modelID = ModelV2.ID.make("model")
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(providerID, (provider) => {
|
||||
provider.request.headers.provider = "provider"
|
||||
provider.request.headers.shared = "provider"
|
||||
provider.request.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.request.headers.model = "model"
|
||||
model.request.headers.shared = "model"
|
||||
model.request.body.model = true
|
||||
model.request.body.request = true
|
||||
model.request.body.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.request.headers).toEqual({ provider: "provider", shared: "model", model: "model" })
|
||||
expect(model.request.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,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ import { Effect, Layer, Schema } from "effect"
|
|||
import { FastCheck } from "effect/testing"
|
||||
import { Config } from "@opencode-ai/core/config"
|
||||
import { ConfigProvider } from "@opencode-ai/core/config/provider"
|
||||
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
||||
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
||||
import { ConfigMigrateV1 } from "@opencode-ai/core/v1/config/migrate"
|
||||
import { ConfigV1 } from "@opencode-ai/core/v1/config/config"
|
||||
import { FSUtil } from "@opencode-ai/core/fs-util"
|
||||
|
|
@ -27,27 +25,25 @@ function testLayer(
|
|||
projectDirectory = directory,
|
||||
vcs?: Project.Vcs,
|
||||
) {
|
||||
const locationLayer = Layer.succeed(
|
||||
Location.Service,
|
||||
Location.Service.of(
|
||||
location(
|
||||
{ directory: AbsolutePath.make(directory) },
|
||||
{ projectDirectory: AbsolutePath.make(projectDirectory), vcs },
|
||||
return Config.locationLayer.pipe(
|
||||
Layer.provide(FSUtil.defaultLayer),
|
||||
Layer.provide(Global.layerWith({ config: globalDirectory })),
|
||||
Layer.provide(
|
||||
Layer.succeed(
|
||||
Location.Service,
|
||||
Location.Service.of(
|
||||
location(
|
||||
{ directory: AbsolutePath.make(directory) },
|
||||
{ projectDirectory: AbsolutePath.make(projectDirectory), vcs },
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
return AppNodeBuilder.build(LayerNode.group([Config.node, Policy.node]), [
|
||||
[Location.node, locationLayer],
|
||||
[Global.node, Global.layerWith({ config: globalDirectory })],
|
||||
])
|
||||
}
|
||||
|
||||
const provider = {
|
||||
api: { type: "native", settings: {} },
|
||||
request: {
|
||||
headers: {},
|
||||
body: {},
|
||||
},
|
||||
package: "test-provider",
|
||||
models: {},
|
||||
}
|
||||
|
||||
|
|
@ -93,6 +89,7 @@ describe("Config", () => {
|
|||
provider: {
|
||||
bedrock: {
|
||||
npm: "@ai-sdk/amazon-bedrock",
|
||||
api: "https://bedrock.example.com",
|
||||
options: {
|
||||
headers: { "x-test": "1" },
|
||||
body: { trace: true },
|
||||
|
|
@ -103,12 +100,9 @@ describe("Config", () => {
|
|||
},
|
||||
})
|
||||
|
||||
expect(migrated.providers?.bedrock?.api).toEqual({
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/amazon-bedrock",
|
||||
settings: { region: "us-east-1", profile: "dev" },
|
||||
})
|
||||
expect(migrated.providers?.bedrock?.request).toEqual({
|
||||
expect(migrated.providers?.bedrock).toMatchObject({
|
||||
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 },
|
||||
})
|
||||
|
|
@ -298,7 +292,7 @@ describe("Config", () => {
|
|||
},
|
||||
tool_output: { max_lines: 1000, max_bytes: 32768 },
|
||||
mcp: {
|
||||
timeout: { startup: 5000, request: 60000 },
|
||||
timeout: { request: 5000 },
|
||||
servers: {
|
||||
local: {
|
||||
type: "local",
|
||||
|
|
@ -313,7 +307,6 @@ describe("Config", () => {
|
|||
headers: { Authorization: "Bearer token" },
|
||||
oauth: { client_id: "client", scope: "read write", callback_port: 19876 },
|
||||
disabled: true,
|
||||
timeout: { startup: 15000 },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -384,7 +377,7 @@ describe("Config", () => {
|
|||
})
|
||||
expect(documents[0]?.info.tool_output).toEqual({ max_lines: 1000, max_bytes: 32768 })
|
||||
expect(documents[0]?.info.mcp).toEqual({
|
||||
timeout: { startup: 5000, request: 60000 },
|
||||
timeout: { request: 5000 },
|
||||
servers: {
|
||||
local: {
|
||||
type: "local",
|
||||
|
|
@ -399,7 +392,6 @@ describe("Config", () => {
|
|||
headers: { Authorization: "Bearer token" },
|
||||
oauth: { client_id: "client", scope: "read write", callback_port: 19876 },
|
||||
disabled: true,
|
||||
timeout: { startup: 15000 },
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
@ -543,12 +535,11 @@ describe("Config", () => {
|
|||
compaction: { auto: true, tail_turns: 3, preserve_recent_tokens: 2000, reserved: 10000 },
|
||||
experimental: { mcp_timeout: 5000 },
|
||||
mcp: {
|
||||
local: { type: "local", command: ["node", "server.js"], enabled: false, timeout: 10000 },
|
||||
local: { type: "local", command: ["node", "server.js"], enabled: false },
|
||||
remote: {
|
||||
type: "remote",
|
||||
url: "https://mcp.example.com",
|
||||
oauth: { clientId: "client", callbackPort: 19876 },
|
||||
timeout: 20000,
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
|
@ -587,34 +578,31 @@ describe("Config", () => {
|
|||
})
|
||||
expect(documents[0]?.info.attachments).toEqual({ image: { auto_resize: false, max_width: 1200 } })
|
||||
expect(documents[0]?.info.providers?.custom).toMatchObject({
|
||||
request: { body: { apiKey: "secret" } },
|
||||
settings: { apiKey: "secret" },
|
||||
models: {
|
||||
model: {
|
||||
request: { body: { reasoningEffort: "high" } },
|
||||
variants: [{ id: "fast", body: { temperature: 0.2 } }],
|
||||
settings: { reasoningEffort: "high" },
|
||||
variants: [{ id: "fast", settings: { temperature: 0.2 } }],
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(documents[0]?.info.providers?.openai).toMatchObject({
|
||||
api: { settings: {} },
|
||||
request: { headers: { Authorization: "Bearer secret", "OpenAI-Organization": "org" } },
|
||||
package: "aisdk:@ai-sdk/openai",
|
||||
settings: { apiKey: "secret", organization: "org" },
|
||||
models: {
|
||||
model: {
|
||||
request: {
|
||||
body: { temperature: 0.3, reasoning: { effort: "high" }, service_tier: "priority" },
|
||||
},
|
||||
variants: [{ id: "high", body: { reasoning: { effort: "high", summary: "auto" } } }],
|
||||
settings: { temperature: 0.3, reasoningEffort: "high", serviceTier: "priority" },
|
||||
variants: [{ id: "high", settings: { reasoningEffort: "high", reasoningSummary: "auto" } }],
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(documents[0]?.info.providers?.anthropic).toMatchObject({
|
||||
models: {
|
||||
model: {
|
||||
request: {
|
||||
body: {
|
||||
output_config: { effort: "high", task_budget: 4096 },
|
||||
metadata: { user_id: "user-1" },
|
||||
},
|
||||
settings: {
|
||||
effort: "high",
|
||||
taskBudget: 4096,
|
||||
metadata: { userId: "user-1" },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -628,17 +616,11 @@ describe("Config", () => {
|
|||
expect(documents[0]?.info.mcp).toMatchObject({
|
||||
timeout: { request: 5000 },
|
||||
servers: {
|
||||
local: {
|
||||
type: "local",
|
||||
command: ["node", "server.js"],
|
||||
disabled: true,
|
||||
timeout: { request: 10000 },
|
||||
},
|
||||
local: { type: "local", command: ["node", "server.js"], disabled: true },
|
||||
remote: {
|
||||
type: "remote",
|
||||
url: "https://mcp.example.com",
|
||||
oauth: { client_id: "client", callback_port: 19876 },
|
||||
timeout: { request: 20000 },
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -2,223 +2,47 @@ import { describe, expect, test } from "bun:test"
|
|||
import { ConfigProviderOptionsV1 } from "@opencode-ai/core/v1/config/provider-options"
|
||||
|
||||
describe("ConfigProviderOptionsV1", () => {
|
||||
test("keeps raw provider and request options unchanged", () => {
|
||||
const lowerer = ConfigProviderOptionsV1.get("custom-provider")
|
||||
|
||||
expect(lowerer.provider({ apiKey: "secret", headers: { "x-test": "1" }, nested: { camelCase: true } })).toEqual({
|
||||
body: { apiKey: "secret", headers: { "x-test": "1" }, nested: { camelCase: true } },
|
||||
})
|
||||
expect(lowerer.request({ nested: { camelCase: true } })).toEqual({ nested: { camelCase: true } })
|
||||
})
|
||||
|
||||
test("falls back to raw lowering for prototype property package names", () => {
|
||||
expect(ConfigProviderOptionsV1.get("toString").provider({ enabled: true })).toEqual({ body: { enabled: true } })
|
||||
})
|
||||
|
||||
test("lowers OpenAI provider and request options", () => {
|
||||
const lowerer = ConfigProviderOptionsV1.get("@ai-sdk/openai")
|
||||
|
||||
test("splits provider overlays without changing package settings", () => {
|
||||
expect(
|
||||
lowerer.provider({
|
||||
ConfigProviderOptionsV1.provider({
|
||||
apiKey: "secret",
|
||||
baseURL: "https://openai.example/v1",
|
||||
organization: "org",
|
||||
project: "project",
|
||||
headers: { "x-test": "1" },
|
||||
headers: { "x-test": "1", invalid: true },
|
||||
body: { store: true },
|
||||
timeout: 1000,
|
||||
nested: { camelCase: true },
|
||||
}),
|
||||
).toEqual({
|
||||
url: "https://openai.example/v1",
|
||||
headers: {
|
||||
Authorization: "Bearer secret",
|
||||
"OpenAI-Organization": "org",
|
||||
"OpenAI-Project": "project",
|
||||
"x-test": "1",
|
||||
settings: {
|
||||
apiKey: "secret",
|
||||
baseURL: "https://openai.example/v1",
|
||||
organization: "org",
|
||||
nested: { camelCase: true },
|
||||
},
|
||||
headers: { "x-test": "1" },
|
||||
body: { store: true },
|
||||
settings: { timeout: 1000 },
|
||||
})
|
||||
})
|
||||
|
||||
test("keeps model and variant options unchanged", () => {
|
||||
expect(
|
||||
lowerer.request({
|
||||
ConfigProviderOptionsV1.model({
|
||||
reasoningEffort: "high",
|
||||
reasoningSummary: "auto",
|
||||
reasoning: { encryptedContent: true },
|
||||
textVerbosity: "low",
|
||||
text: { outputFormat: "plain" },
|
||||
nestedValue: { camelCase: true },
|
||||
}),
|
||||
).toEqual({
|
||||
reasoning: { encrypted_content: true, effort: "high", summary: "auto" },
|
||||
text: { output_format: "plain", verbosity: "low" },
|
||||
nested_value: { camel_case: true },
|
||||
})
|
||||
})
|
||||
|
||||
test("lowers Anthropic provider and request options", () => {
|
||||
const lowerer = ConfigProviderOptionsV1.get("@ai-sdk/anthropic")
|
||||
|
||||
expect(
|
||||
lowerer.provider({
|
||||
apiKey: "secret",
|
||||
authToken: "token",
|
||||
baseURL: "https://anthropic.example",
|
||||
headers: { "x-test": "1" },
|
||||
body: { beta: true },
|
||||
generateId: "custom",
|
||||
}),
|
||||
).toEqual({
|
||||
url: "https://anthropic.example",
|
||||
headers: { "x-api-key": "secret", Authorization: "Bearer token", "x-test": "1" },
|
||||
body: { beta: true },
|
||||
settings: { generateId: "custom" },
|
||||
})
|
||||
expect(
|
||||
lowerer.request({
|
||||
effort: "high",
|
||||
taskBudget: 1024,
|
||||
metadata: { userId: "user", traceId: "trace" },
|
||||
nestedValue: { camelCase: true },
|
||||
metadata: { userId: "user" },
|
||||
}),
|
||||
).toEqual({
|
||||
output_config: { effort: "high", task_budget: 1024 },
|
||||
metadata: { user_id: "user", trace_id: "trace" },
|
||||
nested_value: { camel_case: true },
|
||||
reasoningEffort: "high",
|
||||
taskBudget: 1024,
|
||||
metadata: { userId: "user" },
|
||||
})
|
||||
})
|
||||
|
||||
test("lowers Google provider and request options", () => {
|
||||
const lowerer = ConfigProviderOptionsV1.get("@ai-sdk/google")
|
||||
|
||||
expect(
|
||||
lowerer.provider({
|
||||
apiKey: "secret",
|
||||
baseURL: "https://google.example",
|
||||
headers: { "x-test": "1" },
|
||||
body: { trace: true },
|
||||
project: "project",
|
||||
}),
|
||||
).toEqual({
|
||||
url: "https://google.example",
|
||||
headers: { "x-goog-api-key": "secret", "x-test": "1" },
|
||||
body: { trace: true },
|
||||
settings: { project: "project" },
|
||||
})
|
||||
expect(
|
||||
lowerer.request({
|
||||
thinkingConfig: { thinkingBudget: 1024 },
|
||||
responseModalities: ["TEXT"],
|
||||
mediaResolution: "high",
|
||||
imageConfig: { aspectRatio: "16:9" },
|
||||
safetySettings: ["safe"],
|
||||
}),
|
||||
).toEqual({
|
||||
safetySettings: ["safe"],
|
||||
generationConfig: {
|
||||
thinkingConfig: { thinkingBudget: 1024 },
|
||||
responseModalities: ["TEXT"],
|
||||
mediaResolution: "high",
|
||||
imageConfig: { aspectRatio: "16:9" },
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("lowers Azure provider options and uses OpenAI request lowering", () => {
|
||||
const lowerer = ConfigProviderOptionsV1.get("@ai-sdk/azure")
|
||||
|
||||
expect(
|
||||
lowerer.provider({
|
||||
apiKey: "secret",
|
||||
baseURL: "https://azure.example",
|
||||
headers: { "x-test": "1" },
|
||||
body: { trace: true },
|
||||
resourceName: "resource",
|
||||
}),
|
||||
).toEqual({
|
||||
url: "https://azure.example",
|
||||
headers: { "api-key": "secret", "x-test": "1" },
|
||||
body: { trace: true },
|
||||
settings: { resourceName: "resource" },
|
||||
})
|
||||
expect(lowerer.request({ reasoningEffort: "high", reasoningSummary: "auto", textVerbosity: "low" })).toEqual({
|
||||
reasoning: { effort: "high", summary: "auto" },
|
||||
text: { verbosity: "low" },
|
||||
})
|
||||
})
|
||||
|
||||
test("lowers Amazon Bedrock provider and request options", () => {
|
||||
const lowerer = ConfigProviderOptionsV1.get("@ai-sdk/amazon-bedrock")
|
||||
|
||||
expect(
|
||||
lowerer.provider({
|
||||
headers: { "x-test": "1" },
|
||||
body: { trace: true },
|
||||
region: "us-east-1",
|
||||
profile: "dev",
|
||||
}),
|
||||
).toEqual({
|
||||
headers: { "x-test": "1" },
|
||||
body: { trace: true },
|
||||
settings: { region: "us-east-1", profile: "dev" },
|
||||
})
|
||||
expect(lowerer.request({ temperature: 0.2 })).toEqual({
|
||||
additionalModelRequestFields: { temperature: 0.2 },
|
||||
})
|
||||
})
|
||||
|
||||
test("lowers OpenAI-compatible provider and request options", () => {
|
||||
const lowerer = ConfigProviderOptionsV1.get("@ai-sdk/openai-compatible")
|
||||
|
||||
expect(
|
||||
lowerer.provider({
|
||||
baseURL: "https://compatible.example/v1",
|
||||
headers: { "x-test": "1" },
|
||||
body: { trace: true },
|
||||
apiKey: "secret",
|
||||
}),
|
||||
).toEqual({
|
||||
url: "https://compatible.example/v1",
|
||||
headers: { "x-test": "1" },
|
||||
body: { trace: true },
|
||||
settings: { apiKey: "secret" },
|
||||
})
|
||||
expect(lowerer.request({ reasoningEffort: "high", serviceTier: "priority" })).toEqual({
|
||||
reasoning_effort: "high",
|
||||
serviceTier: "priority",
|
||||
})
|
||||
})
|
||||
|
||||
test.each([
|
||||
"@ai-sdk/cerebras",
|
||||
"@ai-sdk/deepinfra",
|
||||
"@ai-sdk/groq",
|
||||
"@ai-sdk/mistral",
|
||||
"@ai-sdk/togetherai",
|
||||
"@ai-sdk/xai",
|
||||
"@openrouter/ai-sdk-provider",
|
||||
"ai-gateway-provider",
|
||||
"venice-ai-sdk-provider",
|
||||
])("uses OpenAI-compatible lowering for %s", (packageName) => {
|
||||
const lowerer = ConfigProviderOptionsV1.get(packageName)
|
||||
|
||||
expect(lowerer.provider({ baseURL: "https://example.test", apiKey: "secret" })).toEqual({
|
||||
url: "https://example.test",
|
||||
test("uses mechanical lowering for custom provider options", () => {
|
||||
expect(ConfigProviderOptionsV1.provider({ enabled: true })).toEqual({
|
||||
settings: { enabled: true },
|
||||
headers: undefined,
|
||||
body: undefined,
|
||||
settings: { apiKey: "secret" },
|
||||
})
|
||||
expect(lowerer.request({ reasoningEffort: "high" })).toEqual({ reasoning_effort: "high" })
|
||||
})
|
||||
|
||||
test.each(["@ai-sdk/google-vertex", "@ai-sdk/google-vertex/anthropic"])(
|
||||
"uses provider family lowering for %s",
|
||||
(packageName) => {
|
||||
const lowerer = ConfigProviderOptionsV1.get(packageName)
|
||||
|
||||
expect(lowerer.provider({ baseURL: "https://example.test", profile: "dev" })).toMatchObject({
|
||||
url: "https://example.test",
|
||||
settings: { profile: "dev" },
|
||||
})
|
||||
},
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -45,13 +45,6 @@ function withEnv<A, E, R>(vars: Record<string, string | undefined>, effect: () =
|
|||
)
|
||||
}
|
||||
|
||||
function request(headers: Record<string, string>, variant?: string) {
|
||||
return {
|
||||
headers,
|
||||
variant,
|
||||
}
|
||||
}
|
||||
|
||||
const decode = Schema.decodeUnknownSync(Config.Info)
|
||||
|
||||
describe("ConfigProviderPlugin.Plugin", () => {
|
||||
|
|
@ -68,7 +61,8 @@ describe("ConfigProviderPlugin.Plugin", () => {
|
|||
info: decode({
|
||||
providers: {
|
||||
opencode: {
|
||||
api: { type: "aisdk", package: "@ai-sdk/openai", url: "https://opencode.test/v1" },
|
||||
package: "aisdk:@ai-sdk/openai",
|
||||
settings: { baseURL: "https://opencode.test/v1" },
|
||||
models: {
|
||||
"alpha-gpt-next": {
|
||||
variants: [
|
||||
|
|
@ -119,7 +113,8 @@ describe("ConfigProviderPlugin.Plugin", () => {
|
|||
info: decode({
|
||||
providers: {
|
||||
opencode: {
|
||||
api: { type: "aisdk", package: "@ai-sdk/openai", url: "https://opencode.test/v1" },
|
||||
package: "aisdk:@ai-sdk/openai",
|
||||
settings: { baseURL: "https://opencode.test/v1" },
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
|
@ -144,7 +139,7 @@ describe("ConfigProviderPlugin.Plugin", () => {
|
|||
yield* addPlugin(config)
|
||||
|
||||
const model = required(yield* catalog.model.get(providerID, modelID))
|
||||
expect(model.variants[0]).toMatchObject({
|
||||
expect(model.variants?.[0]).toMatchObject({
|
||||
id: "high",
|
||||
body: { reasoningEffort: "high" },
|
||||
})
|
||||
|
|
@ -169,8 +164,8 @@ describe("ConfigProviderPlugin.Plugin", () => {
|
|||
custom: {
|
||||
name: "Configured",
|
||||
env: ["CUSTOM_API_KEY"],
|
||||
api: { type: "native", settings: {} },
|
||||
request: request({ first: "first", shared: "first" }),
|
||||
package: "native",
|
||||
headers: { first: "first", shared: "first" },
|
||||
models: {
|
||||
chat: {
|
||||
name: "First",
|
||||
|
|
@ -178,7 +173,8 @@ describe("ConfigProviderPlugin.Plugin", () => {
|
|||
disabled: true,
|
||||
limit: { context: 100, output: 50 },
|
||||
cost: { input: 1, output: 2 },
|
||||
request: request({ first: "first", shared: "first" }, "retained"),
|
||||
settings: { retained: true },
|
||||
headers: { first: "first", shared: "first" },
|
||||
variants: [
|
||||
{
|
||||
id: "fast",
|
||||
|
|
@ -197,17 +193,18 @@ describe("ConfigProviderPlugin.Plugin", () => {
|
|||
model: "custom/default",
|
||||
providers: {
|
||||
custom: {
|
||||
api: { type: "aisdk", package: "custom-sdk", url: "https://example.test" },
|
||||
request: request({ last: "last", shared: "last" }),
|
||||
package: "aisdk:custom-sdk",
|
||||
settings: { baseURL: "https://example.test" },
|
||||
headers: { last: "last", shared: "last" },
|
||||
models: {
|
||||
default: {
|
||||
name: "Default",
|
||||
},
|
||||
chat: {
|
||||
api: { id: "api-chat" },
|
||||
modelID: "api-chat",
|
||||
name: "Last",
|
||||
limit: { output: 75 },
|
||||
request: request({ last: "last", shared: "last" }),
|
||||
headers: { last: "last", shared: "last" },
|
||||
variants: [
|
||||
{
|
||||
id: "fast",
|
||||
|
|
@ -247,22 +244,24 @@ describe("ConfigProviderPlugin.Plugin", () => {
|
|||
})
|
||||
expect((yield* integrations.get(Integration.ID.make("custom")))?.name).toBe("Renamed")
|
||||
expect(provider.disabled).toBeUndefined()
|
||||
expect(provider.api).toEqual({ type: "aisdk", package: "custom-sdk", url: "https://example.test" })
|
||||
expect(provider.request.headers).toEqual({ first: "first", shared: "last", last: "last" })
|
||||
expect(model.api.id).toBe(ModelV2.ID.make("api-chat"))
|
||||
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)
|
||||
expect(model.modelID).toBe(ModelV2.ID.make("api-chat"))
|
||||
expect(model.name).toBe("Last")
|
||||
expect(model.capabilities).toEqual({ tools: true, input: ["text"], output: ["text"] })
|
||||
expect(model.enabled).toBe(false)
|
||||
expect(model.limit).toEqual({ context: 100, output: 75 })
|
||||
expect(model.cost).toEqual([{ input: 1, output: 2, cache: { read: 0, write: 0 }, tier: undefined }])
|
||||
expect(model.request.headers).toEqual({ first: "first", shared: "last", last: "last" })
|
||||
expect(model.request.variant).toBe("retained")
|
||||
expect(model.variants.map((variant) => variant.id)).toEqual([
|
||||
expect(model.settings).toEqual({ baseURL: "https://example.test", retained: true })
|
||||
expect(model.headers).toEqual({ first: "first", shared: "last", last: "last" })
|
||||
expect(model.variants?.map((variant) => variant.id)).toEqual([
|
||||
ModelV2.VariantID.make("fast"),
|
||||
ModelV2.VariantID.make("slow"),
|
||||
])
|
||||
expect(model.variants[0]?.headers).toEqual({ first: "first", shared: "last", last: "last" })
|
||||
expect(model.variants[1]?.headers).toEqual({ slow: "slow" })
|
||||
expect(model.variants?.[0]?.headers).toEqual({ first: "first", shared: "last", last: "last" })
|
||||
expect(model.variants?.[1]?.headers).toEqual({ slow: "slow" })
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ describe("LocationServiceMap", () => {
|
|||
providers: {
|
||||
unavailable: {
|
||||
name: "Unavailable",
|
||||
api: { type: "native", settings: {} },
|
||||
package: "test-provider",
|
||||
models: { chat: { disabled: true } },
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ export function catalogHost(catalog: Catalog.Interface): PluginContext["catalog"
|
|||
id: ModelV2.ID.make(current.id),
|
||||
providerID: ProviderV2.ID.make(current.providerID),
|
||||
family: current.family === undefined ? undefined : ModelV2.Family.make(current.family),
|
||||
variants: current.variants.map((variant) => ({
|
||||
variants: current.variants?.map((variant) => ({
|
||||
...variant,
|
||||
id: ModelV2.VariantID.make(variant.id),
|
||||
})),
|
||||
|
|
@ -272,29 +272,28 @@ function agentInfo(value: AgentV2.Info) {
|
|||
function providerInfo(value: ProviderV2.MutableInfo) {
|
||||
return {
|
||||
...value,
|
||||
api: { ...value.api, settings: value.api.settings && { ...value.api.settings } },
|
||||
request: { headers: { ...value.request.headers }, body: { ...value.request.body } },
|
||||
settings: value.settings && { ...value.settings },
|
||||
headers: value.headers && { ...value.headers },
|
||||
body: value.body && { ...value.body },
|
||||
}
|
||||
}
|
||||
|
||||
function modelInfo(value: ModelV2.Info | ModelV2.MutableInfo) {
|
||||
return {
|
||||
...value,
|
||||
api: { ...value.api, settings: value.api.settings && { ...value.api.settings } },
|
||||
settings: value.settings && { ...value.settings },
|
||||
headers: value.headers && { ...value.headers },
|
||||
body: value.body && { ...value.body },
|
||||
capabilities: {
|
||||
...value.capabilities,
|
||||
input: [...value.capabilities.input],
|
||||
output: [...value.capabilities.output],
|
||||
},
|
||||
request: {
|
||||
...value.request,
|
||||
headers: { ...value.request.headers },
|
||||
body: { ...value.request.body },
|
||||
},
|
||||
variants: value.variants.map((variant) => ({
|
||||
variants: value.variants?.map((variant) => ({
|
||||
...variant,
|
||||
headers: { ...variant.headers },
|
||||
body: { ...variant.body },
|
||||
settings: variant.settings && { ...variant.settings },
|
||||
headers: variant.headers && { ...variant.headers },
|
||||
body: variant.body && { ...variant.body },
|
||||
})),
|
||||
time: { ...value.time },
|
||||
cost: value.cost.map((cost) => ({ ...cost, tier: cost.tier && { ...cost.tier }, cache: { ...cost.cache } })),
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ describe("AlibabaPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("alibaba"), ModelV2.ID.make("qwen")),
|
||||
api: { id: ModelV2.ID.make("qwen"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("qwen"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/alibaba",
|
||||
options: { name: "alibaba" },
|
||||
|
|
@ -45,7 +46,8 @@ describe("AlibabaPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("alibaba"), ModelV2.ID.make("qwen")),
|
||||
api: { id: ModelV2.ID.make("qwen"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("qwen"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: { name: "alibaba" },
|
||||
|
|
@ -62,7 +64,8 @@ describe("AlibabaPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom-alibaba"), ModelV2.ID.make("qwen")),
|
||||
api: { id: ModelV2.ID.make("qwen"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("qwen"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/alibaba",
|
||||
options: { name: "custom-alibaba", apiKey: "test" },
|
||||
|
|
@ -74,17 +77,18 @@ describe("AlibabaPlugin", () => {
|
|||
}),
|
||||
)
|
||||
|
||||
it.effect("uses the old default languageModel(api.id) behavior", () =>
|
||||
it.effect("uses the default languageModel(modelID) behavior", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const aisdk = yield* AISDK.Service
|
||||
yield* addPlugin()
|
||||
const item = ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("alibaba"), ModelV2.ID.make("alias")),
|
||||
api: { id: ModelV2.ID.make("qwen-plus"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("qwen-plus"),
|
||||
package: "aisdk:test-provider",
|
||||
})
|
||||
const result = yield* aisdk.runSDK({ model: item, package: "@ai-sdk/alibaba", options: {} })
|
||||
const language = result.sdk?.languageModel(item.api.id)
|
||||
const language = result.sdk?.languageModel(item.modelID ?? item.id)
|
||||
expect(language?.modelId).toBe("qwen-plus")
|
||||
expect(language?.provider).toBe("alibaba.chat")
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -79,31 +79,30 @@ function openAIUrl(language: unknown, path: string, modelId: string) {
|
|||
}
|
||||
|
||||
describe("AmazonBedrockPlugin", () => {
|
||||
it.effect("moves endpoint option to api URL", () =>
|
||||
it.effect("moves endpoint option to baseURL", () =>
|
||||
Effect.gen(function* () {
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) => {
|
||||
const bedrock = ProviderV2.Info.make({
|
||||
...ProviderV2.Info.empty(ProviderV2.ID.amazonBedrock),
|
||||
api: { type: "aisdk", package: "@ai-sdk/amazon-bedrock" },
|
||||
request: {
|
||||
headers: {},
|
||||
body: { endpoint: "https://bedrock.example" },
|
||||
},
|
||||
package: "aisdk:@ai-sdk/amazon-bedrock",
|
||||
headers: {},
|
||||
settings: { endpoint: "https://bedrock.example" },
|
||||
})
|
||||
catalog.provider.update(bedrock.id, (item) => {
|
||||
item.api = bedrock.api
|
||||
item.request = bedrock.request
|
||||
item.package = bedrock.package
|
||||
item.package = bedrock.package
|
||||
item.headers = bedrock.headers
|
||||
item.settings = { endpoint: "https://bedrock.example" }
|
||||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
const result = required(yield* catalog.provider.get(ProviderV2.ID.amazonBedrock))
|
||||
expect(result.api).toEqual({
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/amazon-bedrock",
|
||||
url: "https://bedrock.example",
|
||||
expect(result).toMatchObject({
|
||||
package: "aisdk:@ai-sdk/amazon-bedrock",
|
||||
settings: { baseURL: "https://bedrock.example" },
|
||||
})
|
||||
expect(result.request.body.endpoint).toBeUndefined()
|
||||
expect(result.settings?.endpoint).toBeUndefined()
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -116,7 +115,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
|
||||
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/amazon-bedrock",
|
||||
options: {
|
||||
|
|
@ -141,7 +141,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
|
||||
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/amazon-bedrock",
|
||||
options: {
|
||||
|
|
@ -175,11 +176,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
|
||||
api: {
|
||||
id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
|
||||
type: "aisdk",
|
||||
package: "test-provider",
|
||||
},
|
||||
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/amazon-bedrock",
|
||||
options: { name: "amazon-bedrock" },
|
||||
|
|
@ -199,7 +197,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
|
||||
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/amazon-bedrock",
|
||||
options: { name: "amazon-bedrock", region: "eu-west-1" },
|
||||
|
|
@ -218,7 +217,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
|
||||
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/amazon-bedrock",
|
||||
options: { name: "amazon-bedrock" },
|
||||
|
|
@ -237,7 +237,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
|
||||
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/amazon-bedrock",
|
||||
options: { name: "amazon-bedrock" },
|
||||
|
|
@ -257,7 +258,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
|
||||
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/amazon-bedrock",
|
||||
options: {
|
||||
|
|
@ -286,7 +288,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
|
||||
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/amazon-bedrock",
|
||||
options: {
|
||||
|
|
@ -314,11 +317,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("openai.gpt-5.5")),
|
||||
api: {
|
||||
id: ModelV2.ID.make("openai.gpt-5.5"),
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/amazon-bedrock/mantle",
|
||||
},
|
||||
modelID: ModelV2.ID.make("openai.gpt-5.5"),
|
||||
package: "aisdk:@ai-sdk/amazon-bedrock/mantle",
|
||||
}),
|
||||
package: "@ai-sdk/amazon-bedrock/mantle",
|
||||
options: {
|
||||
|
|
@ -345,11 +345,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("openai.gpt-5.5")),
|
||||
api: {
|
||||
id: ModelV2.ID.make("openai.gpt-5.5"),
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/amazon-bedrock/mantle",
|
||||
},
|
||||
modelID: ModelV2.ID.make("openai.gpt-5.5"),
|
||||
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" },
|
||||
|
|
@ -357,11 +354,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("openai.gpt-oss-safeguard-120b")),
|
||||
api: {
|
||||
id: ModelV2.ID.make("openai.gpt-oss-safeguard-120b"),
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/amazon-bedrock/mantle",
|
||||
},
|
||||
modelID: ModelV2.ID.make("openai.gpt-oss-safeguard-120b"),
|
||||
package: "aisdk:@ai-sdk/amazon-bedrock/mantle",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: { region: "us-east-1" },
|
||||
|
|
@ -378,11 +372,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
|
||||
api: {
|
||||
id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/amazon-bedrock/anthropic",
|
||||
},
|
||||
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
|
||||
package: "aisdk:@ai-sdk/amazon-bedrock/anthropic",
|
||||
}),
|
||||
package: "@ai-sdk/amazon-bedrock/anthropic",
|
||||
options: { name: "amazon-bedrock" },
|
||||
|
|
@ -409,11 +400,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
|
||||
api: {
|
||||
id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
|
||||
type: "aisdk",
|
||||
package: "test-provider",
|
||||
},
|
||||
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/amazon-bedrock",
|
||||
options: {
|
||||
|
|
@ -444,7 +432,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
|
||||
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
|
||||
options: {},
|
||||
|
|
@ -452,7 +441,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
|
||||
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
|
||||
options: { region: "eu-west-1" },
|
||||
|
|
@ -460,11 +450,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("global.anthropic.claude-sonnet-4-5")),
|
||||
api: {
|
||||
id: ModelV2.ID.make("global.anthropic.claude-sonnet-4-5"),
|
||||
type: "aisdk",
|
||||
package: "test-provider",
|
||||
},
|
||||
modelID: ModelV2.ID.make("global.anthropic.claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
|
||||
options: { region: "eu-west-1" },
|
||||
|
|
@ -472,7 +459,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
|
||||
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
|
||||
options: { region: "ap-northeast-1" },
|
||||
|
|
@ -480,7 +468,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
|
||||
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
|
||||
options: { region: "ap-southeast-2" },
|
||||
|
|
@ -505,7 +494,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
|
||||
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
|
||||
options: {},
|
||||
|
|
@ -591,7 +581,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make(item.modelID)),
|
||||
api: { id: ModelV2.ID.make(item.modelID), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make(item.modelID),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
|
||||
options: { region: item.region },
|
||||
|
|
@ -610,7 +601,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
const result = yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.openai, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
|
||||
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
|
||||
options: { region: "eu-west-1" },
|
||||
|
|
|
|||
|
|
@ -31,19 +31,21 @@ describe("AnthropicPlugin", () => {
|
|||
yield* catalog.transform((catalog) => {
|
||||
const item = ProviderV2.Info.make({
|
||||
...ProviderV2.Info.empty(ProviderV2.ID.anthropic),
|
||||
api: { type: "aisdk", package: "@ai-sdk/anthropic" },
|
||||
request: { headers: { Existing: "1" }, body: {} },
|
||||
package: "aisdk:@ai-sdk/anthropic",
|
||||
headers: { Existing: "1" },
|
||||
settings: {},
|
||||
})
|
||||
catalog.provider.update(item.id, (draft) => {
|
||||
draft.api = item.api
|
||||
draft.request = item.request
|
||||
draft.package = item.package
|
||||
draft.package = item.package
|
||||
draft.headers = item.headers
|
||||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.anthropic)).request.headers["anthropic-beta"]).toBe(
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.anthropic)).headers?.["anthropic-beta"]).toBe(
|
||||
"interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14",
|
||||
)
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.anthropic)).request.headers.Existing).toBe("1")
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.anthropic)).headers?.Existing).toBe("1")
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -52,9 +54,7 @@ describe("AnthropicPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) => catalog.provider.update(ProviderV2.ID.openai, () => {}))
|
||||
yield* addPlugin()
|
||||
expect(
|
||||
required(yield* catalog.provider.get(ProviderV2.ID.openai)).request.headers["anthropic-beta"],
|
||||
).toBeUndefined()
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.openai)).headers?.["anthropic-beta"]).toBeUndefined()
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -66,7 +66,8 @@ describe("AnthropicPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom-anthropic"), ModelV2.ID.make("claude-sonnet-4-5")),
|
||||
api: { id: ModelV2.ID.make("claude-sonnet-4-5"), type: "aisdk", package: "@ai-sdk/anthropic" },
|
||||
modelID: ModelV2.ID.make("claude-sonnet-4-5"),
|
||||
package: "aisdk:@ai-sdk/anthropic",
|
||||
}),
|
||||
package: "@ai-sdk/anthropic",
|
||||
options: { name: "custom-anthropic", apiKey: "test" },
|
||||
|
|
@ -83,7 +84,8 @@ describe("AnthropicPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.anthropic, ModelV2.ID.make("claude-sonnet-4-5")),
|
||||
api: { id: ModelV2.ID.make("claude-sonnet-4-5"), type: "aisdk", package: "@ai-sdk/anthropic" },
|
||||
modelID: ModelV2.ID.make("claude-sonnet-4-5"),
|
||||
package: "aisdk:@ai-sdk/anthropic",
|
||||
}),
|
||||
package: "@ai-sdk/anthropic",
|
||||
options: { name: "anthropic", apiKey: "test" },
|
||||
|
|
|
|||
|
|
@ -66,18 +66,16 @@ describe("AzureCognitiveServicesPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.make("azure-cognitive-services"), (item) => {
|
||||
item.api = { type: "aisdk", 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.api).toEqual({
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://cognitive.cognitiveservices.azure.com/openai",
|
||||
expect(result).toMatchObject({
|
||||
package: "aisdk:@ai-sdk/openai-compatible",
|
||||
settings: { baseURL: "https://cognitive.cognitiveservices.azure.com/openai" },
|
||||
})
|
||||
expect(result.request.body.baseURL).toBeUndefined()
|
||||
expect(result.request.body.resourceName).toBeUndefined()
|
||||
expect(result.settings?.resourceName).toBeUndefined()
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -89,26 +87,28 @@ describe("AzureCognitiveServicesPlugin", () => {
|
|||
yield* catalog.transform((catalog) => {
|
||||
const azure = ProviderV2.Info.make({
|
||||
...ProviderV2.Info.empty(ProviderV2.ID.make("azure-cognitive-services")),
|
||||
api: { type: "aisdk", package: "@ai-sdk/openai-compatible" },
|
||||
package: "aisdk:@ai-sdk/openai-compatible",
|
||||
})
|
||||
const openai = ProviderV2.Info.make({
|
||||
...ProviderV2.Info.empty(ProviderV2.ID.openai),
|
||||
api: { type: "aisdk", package: "test-provider" },
|
||||
package: "aisdk:test-provider",
|
||||
})
|
||||
catalog.provider.update(azure.id, (item) => {
|
||||
item.api = azure.api
|
||||
item.package = azure.package
|
||||
item.package = azure.package
|
||||
})
|
||||
catalog.provider.update(openai.id, (item) => {
|
||||
item.api = openai.api
|
||||
item.package = openai.package
|
||||
item.package = openai.package
|
||||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
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.request.body.baseURL).toBeUndefined()
|
||||
expect(azure.api).toEqual({ type: "aisdk", package: "@ai-sdk/openai-compatible" })
|
||||
expect(openai.request.body.baseURL).toBeUndefined()
|
||||
expect(openai.api).toEqual({ type: "aisdk", package: "test-provider" })
|
||||
expect(azure.settings?.baseURL).toBeUndefined()
|
||||
expect(azure).toMatchObject({ package: "aisdk:@ai-sdk/openai-compatible" })
|
||||
expect(openai.settings?.baseURL).toBeUndefined()
|
||||
expect(openai).toMatchObject({ package: "aisdk:test-provider" })
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -122,7 +122,8 @@ describe("AzureCognitiveServicesPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("azure-cognitive-services"), ModelV2.ID.make("deployment")),
|
||||
api: { id: ModelV2.ID.make("deployment"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("deployment"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: { useCompletionUrls: true },
|
||||
|
|
@ -140,7 +141,8 @@ describe("AzureCognitiveServicesPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("azure-cognitive-services"), ModelV2.ID.make("deployment")),
|
||||
api: { id: ModelV2.ID.make("deployment"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("deployment"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
@ -148,7 +150,8 @@ describe("AzureCognitiveServicesPlugin", () => {
|
|||
const ignored = yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.openai, ModelV2.ID.make("deployment")),
|
||||
api: { id: ModelV2.ID.make("deployment"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("deployment"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
@ -168,7 +171,8 @@ describe("AzureCognitiveServicesPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("azure-cognitive-services"), ModelV2.ID.make("messages-deployment")),
|
||||
api: { id: ModelV2.ID.make("messages-deployment"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("messages-deployment"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: { messages: sdk.messages, chat: sdk.chat, languageModel: sdk.languageModel },
|
||||
options: {},
|
||||
|
|
@ -176,7 +180,8 @@ describe("AzureCognitiveServicesPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("azure-cognitive-services"), ModelV2.ID.make("chat-deployment")),
|
||||
api: { id: ModelV2.ID.make("chat-deployment"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("chat-deployment"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: { chat: sdk.chat, languageModel: sdk.languageModel },
|
||||
options: {},
|
||||
|
|
@ -184,7 +189,8 @@ describe("AzureCognitiveServicesPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("azure-cognitive-services"), ModelV2.ID.make("language-deployment")),
|
||||
api: { id: ModelV2.ID.make("language-deployment"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("language-deployment"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: { languageModel: sdk.languageModel },
|
||||
options: {},
|
||||
|
|
|
|||
|
|
@ -66,11 +66,11 @@ describe("AzurePlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.azure, (item) => {
|
||||
item.api = { type: "aisdk", package: "@ai-sdk/azure" }
|
||||
item.package = ProviderV2.aisdk("@ai-sdk/azure")
|
||||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.azure)).request.body.resourceName).toBe("from-env")
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.azure)).settings?.resourceName).toBe("from-env")
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -82,18 +82,21 @@ describe("AzurePlugin", () => {
|
|||
yield* catalog.transform((catalog) => {
|
||||
const azure = ProviderV2.Info.make({
|
||||
...ProviderV2.Info.empty(ProviderV2.ID.azure),
|
||||
api: { type: "aisdk", package: "@ai-sdk/azure" },
|
||||
request: { headers: {}, body: { resourceName: "from-config" } },
|
||||
package: "aisdk:@ai-sdk/azure",
|
||||
headers: {},
|
||||
settings: { resourceName: "from-config" },
|
||||
})
|
||||
catalog.provider.update(azure.id, (item) => {
|
||||
item.api = azure.api
|
||||
item.request = azure.request
|
||||
item.package = azure.package
|
||||
item.package = azure.package
|
||||
item.headers = azure.headers
|
||||
item.settings = { resourceName: "from-config" }
|
||||
})
|
||||
catalog.provider.update(ProviderV2.ID.openai, () => {})
|
||||
})
|
||||
yield* addPlugin()
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.azure)).request.body.resourceName).toBe("from-config")
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.openai)).request.body.resourceName).toBeUndefined()
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.azure)).settings?.resourceName).toBe("from-config")
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.openai)).settings?.resourceName).toBeUndefined()
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -105,16 +108,19 @@ describe("AzurePlugin", () => {
|
|||
yield* catalog.transform((catalog) => {
|
||||
const azure = ProviderV2.Info.make({
|
||||
...ProviderV2.Info.empty(ProviderV2.ID.azure),
|
||||
api: { type: "aisdk", package: "@ai-sdk/azure" },
|
||||
request: { headers: {}, body: { resourceName: "" } },
|
||||
package: "aisdk:@ai-sdk/azure",
|
||||
headers: {},
|
||||
settings: { resourceName: "" },
|
||||
})
|
||||
catalog.provider.update(azure.id, (item) => {
|
||||
item.api = azure.api
|
||||
item.request = azure.request
|
||||
item.package = azure.package
|
||||
item.package = azure.package
|
||||
item.headers = azure.headers
|
||||
item.settings = { resourceName: "" }
|
||||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.azure)).request.body.resourceName).toBe("from-env")
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.azure)).settings?.resourceName).toBe("from-env")
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -126,16 +132,19 @@ describe("AzurePlugin", () => {
|
|||
yield* catalog.transform((catalog) => {
|
||||
const azure = ProviderV2.Info.make({
|
||||
...ProviderV2.Info.empty(ProviderV2.ID.azure),
|
||||
api: { type: "aisdk", package: "@ai-sdk/azure" },
|
||||
request: { headers: {}, body: { resourceName: " " } },
|
||||
package: "aisdk:@ai-sdk/azure",
|
||||
headers: {},
|
||||
settings: { resourceName: " " },
|
||||
})
|
||||
catalog.provider.update(azure.id, (item) => {
|
||||
item.api = azure.api
|
||||
item.request = azure.request
|
||||
item.package = azure.package
|
||||
item.package = azure.package
|
||||
item.headers = azure.headers
|
||||
item.settings = { resourceName: " " }
|
||||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.azure)).request.body.resourceName).toBe("from-env")
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.azure)).settings?.resourceName).toBe("from-env")
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -149,7 +158,8 @@ describe("AzurePlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("deployment")),
|
||||
api: { id: ModelV2.ID.make("deployment"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("deployment"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/azure",
|
||||
options: { name: "azure", baseURL: "https://proxy.example.com/openai" },
|
||||
|
|
@ -168,7 +178,8 @@ describe("AzurePlugin", () => {
|
|||
.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("deployment")),
|
||||
api: { id: ModelV2.ID.make("deployment"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("deployment"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/azure",
|
||||
options: { name: "azure" },
|
||||
|
|
@ -188,7 +199,8 @@ describe("AzurePlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("deployment")),
|
||||
api: { id: ModelV2.ID.make("deployment"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("deployment"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: { useCompletionUrls: true },
|
||||
|
|
@ -206,7 +218,8 @@ describe("AzurePlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("deployment")),
|
||||
api: { id: ModelV2.ID.make("deployment"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("deployment"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: { useCompletionUrls: true },
|
||||
|
|
@ -224,8 +237,10 @@ describe("AzurePlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("deployment")),
|
||||
api: { id: ModelV2.ID.make("deployment"), type: "aisdk", package: "test-provider" },
|
||||
request: { headers: {}, body: { useCompletionUrls: true } },
|
||||
modelID: ModelV2.ID.make("deployment"),
|
||||
package: "aisdk:test-provider",
|
||||
headers: {},
|
||||
settings: { useCompletionUrls: true },
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
@ -243,7 +258,8 @@ describe("AzurePlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("deployment")),
|
||||
api: { id: ModelV2.ID.make("deployment"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("deployment"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
@ -251,7 +267,8 @@ describe("AzurePlugin", () => {
|
|||
const ignored = yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.openai, ModelV2.ID.make("deployment")),
|
||||
api: { id: ModelV2.ID.make("deployment"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("deployment"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
@ -274,7 +291,8 @@ describe("AzurePlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("messages-deployment")),
|
||||
api: { id: ModelV2.ID.make("messages-deployment"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("messages-deployment"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: { messages: make("messages"), chat: make("chat"), languageModel: make("languageModel") },
|
||||
options: {},
|
||||
|
|
@ -282,7 +300,8 @@ describe("AzurePlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("language-deployment")),
|
||||
api: { id: ModelV2.ID.make("language-deployment"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("language-deployment"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: { languageModel: make("languageModel") },
|
||||
options: {},
|
||||
|
|
|
|||
|
|
@ -36,12 +36,12 @@ describe("CerebrasPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.make("cerebras"), (item) => {
|
||||
item.api = { type: "aisdk", package: "@ai-sdk/cerebras" }
|
||||
item.request.headers.Existing = "1"
|
||||
item.package = ProviderV2.aisdk("@ai-sdk/cerebras")
|
||||
item.headers = { ...item.headers, Existing: "1" }
|
||||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("cerebras")))?.request.headers).toEqual({
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("cerebras")))?.headers).toEqual({
|
||||
Existing: "1",
|
||||
"X-Cerebras-3rd-Party-Integration": "opencode",
|
||||
})
|
||||
|
|
@ -53,7 +53,7 @@ describe("CerebrasPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) => catalog.provider.update(ProviderV2.ID.make("groq"), () => {}))
|
||||
yield* addPlugin()
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("groq")))?.request.headers).toEqual({})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("groq")))?.headers).toBeUndefined()
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -69,11 +69,8 @@ describe("CerebrasPlugin", () => {
|
|||
ProviderV2.ID.make("custom-cerebras"),
|
||||
ModelV2.ID.make("llama-4-scout-17b-16e-instruct"),
|
||||
),
|
||||
api: {
|
||||
id: ModelV2.ID.make("llama-4-scout-17b-16e-instruct"),
|
||||
type: "aisdk",
|
||||
package: "test-provider",
|
||||
},
|
||||
modelID: ModelV2.ID.make("llama-4-scout-17b-16e-instruct"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/cerebras",
|
||||
options: { name: "custom-cerebras", apiKey: "test" },
|
||||
|
|
@ -95,11 +92,8 @@ describe("CerebrasPlugin", () => {
|
|||
ProviderV2.ID.make("custom-cerebras"),
|
||||
ModelV2.ID.make("llama-4-scout-17b-16e-instruct"),
|
||||
),
|
||||
api: {
|
||||
id: ModelV2.ID.make("llama-4-scout-17b-16e-instruct"),
|
||||
type: "aisdk",
|
||||
package: "test-provider",
|
||||
},
|
||||
modelID: ModelV2.ID.make("llama-4-scout-17b-16e-instruct"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/cerebras",
|
||||
options: { name: "configured-cerebras", apiKey: "test" },
|
||||
|
|
@ -120,11 +114,8 @@ describe("CerebrasPlugin", () => {
|
|||
ProviderV2.ID.make("custom-cerebras"),
|
||||
ModelV2.ID.make("llama-4-scout-17b-16e-instruct"),
|
||||
),
|
||||
api: {
|
||||
id: ModelV2.ID.make("llama-4-scout-17b-16e-instruct"),
|
||||
type: "aisdk",
|
||||
package: "test-provider",
|
||||
},
|
||||
modelID: ModelV2.ID.make("llama-4-scout-17b-16e-instruct"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/groq",
|
||||
options: { name: "custom-cerebras", apiKey: "test" },
|
||||
|
|
|
|||
|
|
@ -118,7 +118,8 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
|
||||
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("openai/gpt-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "ai-gateway-provider",
|
||||
options: { name: "cloudflare-ai-gateway" },
|
||||
|
|
@ -139,7 +140,8 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
|
||||
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("openai/gpt-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "ai-gateway-provider",
|
||||
options: {
|
||||
|
|
@ -183,7 +185,8 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
|
||||
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("openai/gpt-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "ai-gateway-provider",
|
||||
options: {
|
||||
|
|
@ -212,7 +215,8 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
|
||||
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("openai/gpt-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "ai-gateway-provider",
|
||||
options: {
|
||||
|
|
@ -249,7 +253,8 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
|
||||
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("openai/gpt-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "ai-gateway-provider",
|
||||
options: {
|
||||
|
|
@ -280,7 +285,8 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
|
||||
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("openai/gpt-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "ai-gateway-provider",
|
||||
options: { name: "cloudflare-ai-gateway" },
|
||||
|
|
@ -302,7 +308,8 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
|
||||
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("openai/gpt-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "ai-gateway-provider",
|
||||
options: { name: "cloudflare-ai-gateway" },
|
||||
|
|
@ -325,7 +332,8 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
|
||||
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("openai/gpt-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "ai-gateway-provider",
|
||||
options: { name: "cloudflare-ai-gateway" },
|
||||
|
|
@ -354,7 +362,8 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
|
||||
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("openai/gpt-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "ai-gateway-provider",
|
||||
options: { name: "cloudflare-ai-gateway", baseURL: "https://proxy.example/v1" },
|
||||
|
|
@ -380,11 +389,8 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
ProviderV2.ID.make("cloudflare-ai-gateway"),
|
||||
ModelV2.ID.make("anthropic/claude-sonnet-4-5"),
|
||||
),
|
||||
api: {
|
||||
id: ModelV2.ID.make("anthropic/claude-sonnet-4-5"),
|
||||
type: "aisdk",
|
||||
package: "test-provider",
|
||||
},
|
||||
modelID: ModelV2.ID.make("anthropic/claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "ai-gateway-provider",
|
||||
options: { name: "cloudflare-ai-gateway" },
|
||||
|
|
@ -412,7 +418,8 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
|
||||
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("openai/gpt-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: { name: "cloudflare-ai-gateway" },
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ describe("CloudflareWorkersAIPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.make("cloudflare-workers-ai"), (provider) => {
|
||||
provider.api = { type: "aisdk", package: "test-provider" }
|
||||
provider.package = ProviderV2.aisdk("test-provider")
|
||||
}),
|
||||
)
|
||||
yield* addPlugin()
|
||||
|
|
@ -95,15 +95,16 @@ describe("CloudflareWorkersAIPlugin", () => {
|
|||
const sdk = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("@cf/model")),
|
||||
api: { id: ModelV2.ID.make("@cf/model"), ...provider.api },
|
||||
modelID: ModelV2.ID.make("@cf/model"),
|
||||
package: provider.package,
|
||||
settings: provider.settings,
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: { name: "cloudflare-workers-ai", headers: { custom: "header" } },
|
||||
})
|
||||
expect(provider.api).toEqual({
|
||||
type: "aisdk",
|
||||
package: "test-provider",
|
||||
url: "https://api.cloudflare.com/client/v4/accounts/acct/ai/v1",
|
||||
expect(provider).toMatchObject({
|
||||
package: "aisdk:test-provider",
|
||||
settings: { baseURL: "https://api.cloudflare.com/client/v4/accounts/acct/ai/v1" },
|
||||
})
|
||||
expect(sdk.sdk).toBeDefined()
|
||||
}),
|
||||
|
|
@ -116,14 +117,14 @@ describe("CloudflareWorkersAIPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.make("cloudflare-workers-ai"), (provider) => {
|
||||
provider.api = { type: "aisdk", package: "test-provider", url: "https://proxy.example/v1" }
|
||||
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"))).api).toEqual({
|
||||
type: "aisdk",
|
||||
package: "test-provider",
|
||||
url: "https://proxy.example/v1",
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("cloudflare-workers-ai")))).toMatchObject({
|
||||
package: "aisdk:test-provider",
|
||||
settings: { baseURL: "https://proxy.example/v1" },
|
||||
})
|
||||
}),
|
||||
),
|
||||
|
|
@ -138,12 +139,9 @@ describe("CloudflareWorkersAIPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("@cf/model")),
|
||||
api: {
|
||||
id: ModelV2.ID.make("@cf/model"),
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://proxy.example/v1",
|
||||
},
|
||||
modelID: ModelV2.ID.make("@cf/model"),
|
||||
package: "aisdk:@ai-sdk/openai-compatible",
|
||||
settings: { baseURL: "https://proxy.example/v1" },
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: { name: "cloudflare-workers-ai", baseURL: "https://proxy.example/v1" },
|
||||
|
|
@ -159,15 +157,14 @@ describe("CloudflareWorkersAIPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.make("cloudflare-workers-ai"), (provider) => {
|
||||
provider.api = { type: "aisdk", package: "test-provider" }
|
||||
provider.request.body.accountId = "configured-acct"
|
||||
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"))).api).toEqual({
|
||||
type: "aisdk",
|
||||
package: "test-provider",
|
||||
url: "https://api.cloudflare.com/client/v4/accounts/env-acct/ai/v1",
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("cloudflare-workers-ai")))).toMatchObject({
|
||||
package: "aisdk:test-provider",
|
||||
settings: { baseURL: "https://api.cloudflare.com/client/v4/accounts/env-acct/ai/v1" },
|
||||
})
|
||||
}),
|
||||
),
|
||||
|
|
@ -182,12 +179,9 @@ describe("CloudflareWorkersAIPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("@cf/model")),
|
||||
api: {
|
||||
id: ModelV2.ID.make("@cf/model"),
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://proxy.example/v1",
|
||||
},
|
||||
modelID: ModelV2.ID.make("@cf/model"),
|
||||
package: "aisdk:@ai-sdk/openai-compatible",
|
||||
settings: { baseURL: "https://proxy.example/v1" },
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: {
|
||||
|
|
@ -214,12 +208,9 @@ describe("CloudflareWorkersAIPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("@cf/model")),
|
||||
api: {
|
||||
id: ModelV2.ID.make("@cf/model"),
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/ai/v1",
|
||||
},
|
||||
modelID: ModelV2.ID.make("@cf/model"),
|
||||
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",
|
||||
options: {
|
||||
|
|
@ -243,7 +234,8 @@ describe("CloudflareWorkersAIPlugin", () => {
|
|||
const result = yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("alias")),
|
||||
api: { id: ModelV2.ID.make("@cf/api-model"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("@cf/api-model"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
@ -262,12 +254,9 @@ describe("CloudflareWorkersAIPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("@cf/model")),
|
||||
api: {
|
||||
id: ModelV2.ID.make("@cf/model"),
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/anthropic",
|
||||
url: "https://proxy.example/v1",
|
||||
},
|
||||
modelID: ModelV2.ID.make("@cf/model"),
|
||||
package: "aisdk:@ai-sdk/anthropic",
|
||||
settings: { baseURL: "https://proxy.example/v1" },
|
||||
}),
|
||||
package: "@ai-sdk/anthropic",
|
||||
options: { name: "cloudflare-workers-ai" },
|
||||
|
|
|
|||
|
|
@ -56,7 +56,8 @@ describe("CoherePlugin", () => {
|
|||
const ignored = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("cohere"), ModelV2.ID.make("command")),
|
||||
api: { id: ModelV2.ID.make("command"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("command"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: { name: "cohere" },
|
||||
|
|
@ -66,7 +67,8 @@ describe("CoherePlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("cohere"), ModelV2.ID.make("command")),
|
||||
api: { id: ModelV2.ID.make("command"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("command"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/cohere",
|
||||
options: { name: "cohere" },
|
||||
|
|
@ -83,7 +85,8 @@ describe("CoherePlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom-cohere"), ModelV2.ID.make("command-r-plus")),
|
||||
api: { id: ModelV2.ID.make("command-r-plus"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("command-r-plus"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/cohere",
|
||||
options: { name: "custom-cohere", apiKey: "test", baseURL: "https://cohere.example" },
|
||||
|
|
@ -108,7 +111,8 @@ describe("CoherePlugin", () => {
|
|||
const result = yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("cohere"), ModelV2.ID.make("alias")),
|
||||
api: { id: ModelV2.ID.make("command-r-plus"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("command-r-plus"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk,
|
||||
options: {},
|
||||
|
|
|
|||
|
|
@ -48,7 +48,8 @@ describe("DeepInfraPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("deepinfra"), ModelV2.ID.make("model")),
|
||||
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "@ai-sdk/deepinfra" },
|
||||
modelID: ModelV2.ID.make("model"),
|
||||
package: "aisdk:@ai-sdk/deepinfra",
|
||||
}),
|
||||
package: "@ai-sdk/deepinfra",
|
||||
options: { name: "deepinfra" },
|
||||
|
|
@ -66,7 +67,8 @@ describe("DeepInfraPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom-deepinfra"), ModelV2.ID.make("model")),
|
||||
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "@ai-sdk/deepinfra" },
|
||||
modelID: ModelV2.ID.make("model"),
|
||||
package: "aisdk:@ai-sdk/deepinfra",
|
||||
}),
|
||||
package: "@ai-sdk/deepinfra",
|
||||
options: { name: "custom-deepinfra", apiKey: "test" },
|
||||
|
|
@ -85,7 +87,8 @@ describe("DeepInfraPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("deepinfra"), ModelV2.ID.make("model")),
|
||||
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "@ai-sdk/deepinfra" },
|
||||
modelID: ModelV2.ID.make("model"),
|
||||
package: "aisdk:@ai-sdk/deepinfra",
|
||||
}),
|
||||
package: "@ai-sdk/deepinfra",
|
||||
options: { name: "deepinfra", apiKey: "test" },
|
||||
|
|
@ -111,7 +114,8 @@ describe("DeepInfraPlugin", () => {
|
|||
const ignored = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("deepinfra"), ModelV2.ID.make("model")),
|
||||
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "@ai-sdk/deepinfra" },
|
||||
modelID: ModelV2.ID.make("model"),
|
||||
package: "aisdk:@ai-sdk/deepinfra",
|
||||
}),
|
||||
package: item,
|
||||
options: { name: "deepinfra" },
|
||||
|
|
@ -122,7 +126,8 @@ describe("DeepInfraPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("deepinfra"), ModelV2.ID.make("model")),
|
||||
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "@ai-sdk/deepinfra" },
|
||||
modelID: ModelV2.ID.make("model"),
|
||||
package: "aisdk:@ai-sdk/deepinfra",
|
||||
}),
|
||||
package: "@ai-sdk/deepinfra",
|
||||
options: { name: "deepinfra" },
|
||||
|
|
@ -141,17 +146,14 @@ describe("DeepInfraPlugin", () => {
|
|||
const sdkEvent = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("deepinfra"), ModelV2.ID.make("meta-llama/Llama-3.3-70B-Instruct")),
|
||||
api: {
|
||||
id: ModelV2.ID.make("meta-llama/Llama-3.3-70B-Instruct"),
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/deepinfra",
|
||||
},
|
||||
modelID: ModelV2.ID.make("meta-llama/Llama-3.3-70B-Instruct"),
|
||||
package: "aisdk:@ai-sdk/deepinfra",
|
||||
}),
|
||||
package: "@ai-sdk/deepinfra",
|
||||
options: { name: "deepinfra" },
|
||||
})
|
||||
const result = yield* aisdk.runLanguage({ model: sdkEvent.model, sdk: sdkEvent.sdk, options: sdkEvent.options })
|
||||
const language = result.language ?? result.sdk.languageModel(result.model.api.id)
|
||||
const language = result.language ?? result.sdk.languageModel(result.model.modelID ?? result.model.id)
|
||||
expect(language.provider).toBe("deepinfra.chat")
|
||||
expect(deepinfraLanguageModels).toEqual(["meta-llama/Llama-3.3-70B-Instruct"])
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -54,7 +54,8 @@ describe("DynamicProviderPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("test-model")),
|
||||
api: { id: ModelV2.ID.make("test-model"), type: "aisdk", package: fixtureProvider },
|
||||
modelID: ModelV2.ID.make("test-model"),
|
||||
package: ProviderV2.aisdk(fixtureProvider),
|
||||
}),
|
||||
package: fixtureProvider,
|
||||
options: { name: "custom", marker: "dynamic" },
|
||||
|
|
@ -72,7 +73,8 @@ describe("DynamicProviderPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("test-model")),
|
||||
api: { id: ModelV2.ID.make("test-model"), type: "aisdk", package: fixtureProvider },
|
||||
modelID: ModelV2.ID.make("test-model"),
|
||||
package: ProviderV2.aisdk(fixtureProvider),
|
||||
}),
|
||||
package: fixtureProvider,
|
||||
options: { name: "custom", marker: "dynamic" },
|
||||
|
|
@ -89,7 +91,8 @@ describe("DynamicProviderPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom-provider"), ModelV2.ID.make("test-model")),
|
||||
api: { id: ModelV2.ID.make("test-model"), type: "aisdk", package: fixtureProvider },
|
||||
modelID: ModelV2.ID.make("test-model"),
|
||||
package: ProviderV2.aisdk(fixtureProvider),
|
||||
}),
|
||||
package: fixtureProvider,
|
||||
options: { name: "custom-provider", marker: "dynamic" },
|
||||
|
|
@ -105,7 +108,8 @@ describe("DynamicProviderPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("npm-provider"), ModelV2.ID.make("test-model")),
|
||||
api: { id: ModelV2.ID.make("test-model"), type: "aisdk", package: "fixture-provider" },
|
||||
modelID: ModelV2.ID.make("test-model"),
|
||||
package: "aisdk:fixture-provider",
|
||||
}),
|
||||
package: "fixture-provider",
|
||||
options: { name: "npm-provider", marker: "npm" },
|
||||
|
|
@ -122,7 +126,8 @@ describe("DynamicProviderPlugin", () => {
|
|||
.language(
|
||||
ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("missing-entrypoint"), ModelV2.ID.make("alias")),
|
||||
api: { id: ModelV2.ID.make("alias"), type: "aisdk", package: "fixture-provider" },
|
||||
modelID: ModelV2.ID.make("alias"),
|
||||
package: "aisdk:fixture-provider",
|
||||
}),
|
||||
)
|
||||
.pipe(Effect.exit)
|
||||
|
|
@ -139,7 +144,8 @@ describe("DynamicProviderPlugin", () => {
|
|||
.language(
|
||||
ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("bad-import"), ModelV2.ID.make("alias")),
|
||||
api: { id: ModelV2.ID.make("alias"), type: "aisdk", package: "file:///missing/provider-factory.js" },
|
||||
modelID: ModelV2.ID.make("alias"),
|
||||
package: "aisdk:file:///missing/provider-factory.js",
|
||||
}),
|
||||
)
|
||||
.pipe(Effect.exit)
|
||||
|
|
@ -158,7 +164,8 @@ describe("DynamicProviderPlugin", () => {
|
|||
.language(
|
||||
ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("missing-factory"), ModelV2.ID.make("alias")),
|
||||
api: { id: ModelV2.ID.make("alias"), type: "aisdk", package: "fixture-provider" },
|
||||
modelID: ModelV2.ID.make("alias"),
|
||||
package: "aisdk:fixture-provider",
|
||||
}),
|
||||
)
|
||||
.pipe(Effect.exit)
|
||||
|
|
@ -167,7 +174,7 @@ describe("DynamicProviderPlugin", () => {
|
|||
}),
|
||||
)
|
||||
|
||||
itWithAISDK.effect("uses the model api.id for the default language model", () =>
|
||||
itWithAISDK.effect("uses the model modelID for the default language model", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const aisdk = yield* AISDK.Service
|
||||
|
|
@ -175,7 +182,8 @@ describe("DynamicProviderPlugin", () => {
|
|||
const language = yield* aisdk.language(
|
||||
ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("alias")),
|
||||
api: { id: ModelV2.ID.make("test-model-api"), type: "aisdk", package: fixtureProvider },
|
||||
modelID: ModelV2.ID.make("test-model-api"),
|
||||
package: ProviderV2.aisdk(fixtureProvider),
|
||||
}),
|
||||
)
|
||||
expect(language).toMatchObject({ modelID: "test-model-api", options: { name: "custom" } })
|
||||
|
|
|
|||
|
|
@ -45,7 +45,8 @@ describe("GatewayPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("gateway"), ModelV2.ID.make("model")),
|
||||
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("model"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/gateway",
|
||||
options: { name: "gateway" },
|
||||
|
|
@ -65,11 +66,8 @@ describe("GatewayPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("vercel"), ModelV2.ID.make("anthropic/claude-sonnet-4")),
|
||||
api: {
|
||||
id: ModelV2.ID.make("anthropic/claude-sonnet-4"),
|
||||
type: "aisdk",
|
||||
package: "test-provider",
|
||||
},
|
||||
modelID: ModelV2.ID.make("anthropic/claude-sonnet-4"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/gateway",
|
||||
options: { name: "vercel", apiKey: "test-key" },
|
||||
|
|
@ -91,7 +89,8 @@ describe("GatewayPlugin", () => {
|
|||
const ignored = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("vercel"), ModelV2.ID.make(modelID)),
|
||||
api: { id: ModelV2.ID.make(modelID), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make(modelID),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/vercel",
|
||||
options: { name: "vercel" },
|
||||
|
|
@ -101,7 +100,8 @@ describe("GatewayPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("vercel"), ModelV2.ID.make(modelID)),
|
||||
api: { id: ModelV2.ID.make(modelID), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make(modelID),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/gateway",
|
||||
options: { name: "vercel" },
|
||||
|
|
|
|||
|
|
@ -47,7 +47,8 @@ describe("GithubCopilotPlugin", () => {
|
|||
const ignored = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-5")),
|
||||
api: { id: ModelV2.ID.make("gpt-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("gpt-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: { name: "github-copilot" },
|
||||
|
|
@ -55,7 +56,8 @@ describe("GithubCopilotPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-5")),
|
||||
api: { id: ModelV2.ID.make("gpt-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("gpt-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/github-copilot",
|
||||
options: { name: "github-copilot" },
|
||||
|
|
@ -74,7 +76,8 @@ describe("GithubCopilotPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("claude-sonnet-4")),
|
||||
api: { id: ModelV2.ID.make("claude-sonnet-4"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("claude-sonnet-4"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
|
||||
options: {},
|
||||
|
|
@ -92,7 +95,8 @@ describe("GithubCopilotPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("alias")),
|
||||
api: { id: ModelV2.ID.make("claude-sonnet-4"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("claude-sonnet-4"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
|
||||
options: {},
|
||||
|
|
@ -110,7 +114,8 @@ describe("GithubCopilotPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-5")),
|
||||
api: { id: ModelV2.ID.make("gpt-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("gpt-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
@ -118,7 +123,8 @@ describe("GithubCopilotPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-5.1-codex")),
|
||||
api: { id: ModelV2.ID.make("gpt-5.1-codex"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("gpt-5.1-codex"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
@ -126,7 +132,8 @@ describe("GithubCopilotPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-4o")),
|
||||
api: { id: ModelV2.ID.make("gpt-4o"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("gpt-4o"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
@ -134,7 +141,8 @@ describe("GithubCopilotPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-5-mini")),
|
||||
api: { id: ModelV2.ID.make("gpt-5-mini"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("gpt-5-mini"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
@ -142,7 +150,8 @@ describe("GithubCopilotPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-5-mini-2025-08-07")),
|
||||
api: { id: ModelV2.ID.make("gpt-5-mini-2025-08-07"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("gpt-5-mini-2025-08-07"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
@ -166,7 +175,8 @@ describe("GithubCopilotPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("default")),
|
||||
api: { id: ModelV2.ID.make("gpt-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("gpt-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
@ -174,7 +184,8 @@ describe("GithubCopilotPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("small")),
|
||||
api: { id: ModelV2.ID.make("gpt-5-mini"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("gpt-5-mini"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
@ -182,7 +193,8 @@ describe("GithubCopilotPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("sonnet")),
|
||||
api: { id: ModelV2.ID.make("claude-sonnet-4"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("claude-sonnet-4"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
@ -230,7 +242,8 @@ describe("GithubCopilotPlugin", () => {
|
|||
const result = yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("openai"), ModelV2.ID.make("gpt-5")),
|
||||
api: { id: ModelV2.ID.make("gpt-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("gpt-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
|
|||
|
|
@ -70,7 +70,8 @@ describe("GitLabPlugin", () => {
|
|||
yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("claude")),
|
||||
api: { id: ModelV2.ID.make("claude"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("claude"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "gitlab-ai-provider",
|
||||
options: { name: "gitlab" },
|
||||
|
|
@ -107,7 +108,8 @@ describe("GitLabPlugin", () => {
|
|||
yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("claude")),
|
||||
api: { id: ModelV2.ID.make("claude"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("claude"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "gitlab-ai-provider",
|
||||
options: { name: "gitlab" },
|
||||
|
|
@ -132,7 +134,8 @@ describe("GitLabPlugin", () => {
|
|||
yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("claude")),
|
||||
api: { id: ModelV2.ID.make("claude"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("claude"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "gitlab-ai-provider",
|
||||
options: {
|
||||
|
|
@ -173,7 +176,8 @@ describe("GitLabPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("claude")),
|
||||
api: { id: ModelV2.ID.make("claude"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("claude"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/openai",
|
||||
options: { name: "gitlab" },
|
||||
|
|
@ -192,11 +196,10 @@ describe("GitLabPlugin", () => {
|
|||
const result = yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("duo-workflow-custom")),
|
||||
api: { id: ModelV2.ID.make("duo-workflow-custom"), type: "aisdk", package: "test-provider" },
|
||||
request: {
|
||||
headers: {},
|
||||
body: { workflowRef: "ref", workflowDefinition: "definition" },
|
||||
},
|
||||
modelID: ModelV2.ID.make("duo-workflow-custom"),
|
||||
package: "aisdk:test-provider",
|
||||
headers: {},
|
||||
settings: { workflowRef: "ref", workflowDefinition: "definition" },
|
||||
}),
|
||||
sdk: {
|
||||
workflowChat: (id: string, options: unknown) => {
|
||||
|
|
@ -227,7 +230,8 @@ describe("GitLabPlugin", () => {
|
|||
const result = yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("duo-workflow-exact")),
|
||||
api: { id: ModelV2.ID.make("duo-workflow-exact"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("duo-workflow-exact"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: {
|
||||
workflowChat: (id: string, options: unknown) => {
|
||||
|
|
@ -245,7 +249,7 @@ describe("GitLabPlugin", () => {
|
|||
}),
|
||||
)
|
||||
|
||||
it.effect("uses provider feature flags instead of request feature flags", () =>
|
||||
it.effect("uses provider feature flags instead of model settings feature flags", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const aisdk = yield* AISDK.Service
|
||||
|
|
@ -254,11 +258,10 @@ describe("GitLabPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("duo-workflow-custom")),
|
||||
api: { id: ModelV2.ID.make("duo-workflow-custom"), type: "aisdk", package: "test-provider" },
|
||||
request: {
|
||||
headers: {},
|
||||
body: { featureFlags: { request_flag: true } },
|
||||
},
|
||||
modelID: ModelV2.ID.make("duo-workflow-custom"),
|
||||
package: "aisdk:test-provider",
|
||||
headers: {},
|
||||
settings: { featureFlags: { request_flag: true } },
|
||||
}),
|
||||
sdk: {
|
||||
workflowChat: (id: string, options: unknown) => {
|
||||
|
|
@ -282,8 +285,10 @@ describe("GitLabPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("claude")),
|
||||
api: { id: ModelV2.ID.make("claude"), type: "aisdk", package: "test-provider" },
|
||||
request: { headers: { h: "v" }, body: {} },
|
||||
modelID: ModelV2.ID.make("claude"),
|
||||
package: "aisdk:test-provider",
|
||||
headers: { h: "v" },
|
||||
settings: {},
|
||||
}),
|
||||
sdk: {
|
||||
workflowChat: () => undefined,
|
||||
|
|
|
|||
|
|
@ -64,16 +64,16 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.make("google-vertex-anthropic"), (provider) => {
|
||||
provider.api = { type: "aisdk", package: "@ai-sdk/google-vertex/anthropic" }
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/google-vertex/anthropic")
|
||||
}),
|
||||
)
|
||||
yield* addPlugin(GoogleVertexAnthropicPlugin)
|
||||
expect(
|
||||
(yield* catalog.provider.get(ProviderV2.ID.make("google-vertex-anthropic")))?.request.body.project,
|
||||
).toBe("cloud-project")
|
||||
expect(
|
||||
(yield* catalog.provider.get(ProviderV2.ID.make("google-vertex-anthropic")))?.request.body.location,
|
||||
).toBe("cloud-location")
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("google-vertex-anthropic")))?.settings?.project).toBe(
|
||||
"cloud-project",
|
||||
)
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("google-vertex-anthropic")))?.settings?.location).toBe(
|
||||
"cloud-location",
|
||||
)
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -84,18 +84,17 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.make("google-vertex-anthropic"), (provider) => {
|
||||
provider.api = { type: "aisdk", package: "@ai-sdk/google-vertex/anthropic" }
|
||||
provider.request.body.project = "configured-project"
|
||||
provider.request.body.location = "configured-location"
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/google-vertex/anthropic")
|
||||
provider.settings = { ...provider.settings, project: "configured-project", location: "configured-location" }
|
||||
}),
|
||||
)
|
||||
yield* addPlugin(GoogleVertexAnthropicPlugin)
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("google-vertex-anthropic")))?.request.body.project).toBe(
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("google-vertex-anthropic")))?.settings?.project).toBe(
|
||||
"configured-project",
|
||||
)
|
||||
expect(
|
||||
(yield* catalog.provider.get(ProviderV2.ID.make("google-vertex-anthropic")))?.request.body.location,
|
||||
).toBe("configured-location")
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("google-vertex-anthropic")))?.settings?.location).toBe(
|
||||
"configured-location",
|
||||
)
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -121,7 +120,8 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||
ProviderV2.ID.make("google-vertex-anthropic"),
|
||||
ModelV2.ID.make("claude-sonnet-4-5"),
|
||||
),
|
||||
api: { id: ModelV2.ID.make("claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/google-vertex/anthropic",
|
||||
options: { name: "google-vertex-anthropic" },
|
||||
|
|
@ -147,7 +147,8 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||
ProviderV2.ID.make("google-vertex-anthropic"),
|
||||
ModelV2.ID.make("claude-sonnet-4-5"),
|
||||
),
|
||||
api: { id: ModelV2.ID.make("claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/google-vertex/anthropic",
|
||||
options: { name: "google-vertex-anthropic" },
|
||||
|
|
@ -167,7 +168,8 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("claude-sonnet-4-5")),
|
||||
api: { id: ModelV2.ID.make("claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/google-vertex/anthropic",
|
||||
options: { name: "google-vertex", project: "project", location: "eu" },
|
||||
|
|
@ -186,7 +188,8 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("claude-sonnet-4-5")),
|
||||
api: { id: ModelV2.ID.make("claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/google-vertex/anthropic",
|
||||
options: { name: "google-vertex", project: "project", location: "eu", baseURL: "https://proxy.example/v1" },
|
||||
|
|
@ -204,7 +207,8 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||
const sdkResult = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make(" claude-sonnet-4-5 ")),
|
||||
api: { id: ModelV2.ID.make(" claude-sonnet-4-5 "), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make(" claude-sonnet-4-5 "),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/google-vertex/anthropic",
|
||||
options: { name: "google-vertex", project: "project", location: "us" },
|
||||
|
|
@ -212,7 +216,8 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||
const languageResult = yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make(" claude-sonnet-4-5 ")),
|
||||
api: { id: ModelV2.ID.make(" claude-sonnet-4-5 "), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make(" claude-sonnet-4-5 "),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: sdkResult.sdk,
|
||||
options: {},
|
||||
|
|
@ -234,7 +239,8 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex-anthropic"), ModelV2.ID.make(" claude-sonnet-4-5 ")),
|
||||
api: { id: ModelV2.ID.make(" claude-sonnet-4-5 "), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make(" claude-sonnet-4-5 "),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: { languageModel: selector(calls) },
|
||||
options: {},
|
||||
|
|
@ -252,7 +258,8 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||
const result = yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("claude-sonnet-4-5")),
|
||||
api: { id: ModelV2.ID.make("claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("claude-sonnet-4-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: { languageModel: selector(calls) },
|
||||
options: {},
|
||||
|
|
|
|||
|
|
@ -92,17 +92,14 @@ describe("GoogleVertexPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.opencode, (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://opencode.ai/zen/v1",
|
||||
}
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
|
||||
provider.settings = { ...provider.settings, baseURL: "https://opencode.ai/zen/v1" }
|
||||
}),
|
||||
)
|
||||
yield* addPlugin()
|
||||
|
||||
const provider = required(yield* catalog.provider.get(ProviderV2.ID.opencode))
|
||||
expect(provider.request.body).toEqual({})
|
||||
expect(provider.settings).toEqual({ baseURL: "https://opencode.ai/zen/v1" })
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -121,21 +118,24 @@ describe("GoogleVertexPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.make("google-vertex"), (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}",
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
|
||||
provider.settings = {
|
||||
...provider.settings,
|
||||
baseURL:
|
||||
"https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}",
|
||||
}
|
||||
}),
|
||||
)
|
||||
yield* addPlugin()
|
||||
const provider = required(yield* catalog.provider.get(ProviderV2.ID.make("google-vertex")))
|
||||
expect(provider.request.body.project).toBe("google-cloud-project")
|
||||
expect(provider.request.body.location).toBe("google-vertex-location")
|
||||
expect(provider.api).toEqual({
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://google-vertex-location-aiplatform.googleapis.com/v1/projects/google-cloud-project/locations/google-vertex-location",
|
||||
expect(provider.settings?.project).toBe("google-cloud-project")
|
||||
expect(provider.settings?.location).toBe("google-vertex-location")
|
||||
expect(provider).toMatchObject({
|
||||
package: "aisdk:@ai-sdk/openai-compatible",
|
||||
settings: {
|
||||
baseURL:
|
||||
"https://google-vertex-location-aiplatform.googleapis.com/v1/projects/google-cloud-project/locations/google-vertex-location",
|
||||
},
|
||||
})
|
||||
}),
|
||||
),
|
||||
|
|
@ -160,10 +160,11 @@ describe("GoogleVertexPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.make("google-vertex"), (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}",
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
|
||||
provider.settings = {
|
||||
...provider.settings,
|
||||
baseURL:
|
||||
"https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}",
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
|
@ -172,21 +173,20 @@ describe("GoogleVertexPlugin", () => {
|
|||
yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("gemini")),
|
||||
api: {
|
||||
id: ModelV2.ID.make("gemini"),
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/google-vertex",
|
||||
},
|
||||
modelID: ModelV2.ID.make("gemini"),
|
||||
package: "aisdk:@ai-sdk/google-vertex",
|
||||
}),
|
||||
package: "@ai-sdk/google-vertex",
|
||||
options: { name: "google-vertex" },
|
||||
})
|
||||
|
||||
expect(provider.request.body.project).toBe("vertex-project")
|
||||
expect(provider.api).toEqual({
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://europe-west4-aiplatform.googleapis.com/v1/projects/vertex-project/locations/europe-west4",
|
||||
expect(provider.settings?.project).toBe("vertex-project")
|
||||
expect(provider).toMatchObject({
|
||||
package: "aisdk:@ai-sdk/openai-compatible",
|
||||
settings: {
|
||||
baseURL:
|
||||
"https://europe-west4-aiplatform.googleapis.com/v1/projects/vertex-project/locations/europe-west4",
|
||||
},
|
||||
})
|
||||
expect(vertexOptions[0].project).toBe("vertex-project")
|
||||
expect(vertexOptions[0].location).toBe("europe-west4")
|
||||
|
|
@ -209,23 +209,22 @@ describe("GoogleVertexPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.make("google-vertex"), (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}",
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
|
||||
provider.settings = {
|
||||
...provider.settings,
|
||||
baseURL:
|
||||
"https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}",
|
||||
}
|
||||
provider.request.body.project = "config-project"
|
||||
provider.request.body.location = "global"
|
||||
provider.settings = { ...provider.settings, project: "config-project", location: "global" }
|
||||
}),
|
||||
)
|
||||
yield* addPlugin()
|
||||
const provider = required(yield* catalog.provider.get(ProviderV2.ID.make("google-vertex")))
|
||||
expect(provider.request.body.project).toBe("config-project")
|
||||
expect(provider.request.body.location).toBe("global")
|
||||
expect(provider.api).toEqual({
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://aiplatform.googleapis.com/v1/projects/config-project/locations/global",
|
||||
expect(provider.settings?.project).toBe("config-project")
|
||||
expect(provider.settings?.location).toBe("global")
|
||||
expect(provider).toMatchObject({
|
||||
package: "aisdk:@ai-sdk/openai-compatible",
|
||||
settings: { baseURL: "https://aiplatform.googleapis.com/v1/projects/config-project/locations/global" },
|
||||
})
|
||||
}),
|
||||
),
|
||||
|
|
@ -236,21 +235,20 @@ describe("GoogleVertexPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.make("google-vertex"), (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}",
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
|
||||
provider.settings = {
|
||||
...provider.settings,
|
||||
baseURL:
|
||||
"https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}",
|
||||
}
|
||||
provider.request.body.project = "config-project"
|
||||
provider.request.body.location = "eu"
|
||||
provider.settings = { ...provider.settings, project: "config-project", location: "eu" }
|
||||
}),
|
||||
)
|
||||
yield* addPlugin()
|
||||
const provider = required(yield* catalog.provider.get(ProviderV2.ID.make("google-vertex")))
|
||||
expect(provider.api).toEqual({
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://eu-aiplatform.googleapis.com/v1/projects/config-project/locations/eu",
|
||||
expect(provider).toMatchObject({
|
||||
package: "aisdk:@ai-sdk/openai-compatible",
|
||||
settings: { baseURL: "https://eu-aiplatform.googleapis.com/v1/projects/config-project/locations/eu" },
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
|
@ -270,14 +268,14 @@ describe("GoogleVertexPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.make("google-vertex"), (provider) => {
|
||||
provider.api = { type: "aisdk", package: "@ai-sdk/google-vertex" }
|
||||
provider.request.body.project = "config-project"
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/google-vertex")
|
||||
provider.settings = { ...provider.settings, project: "config-project" }
|
||||
}),
|
||||
)
|
||||
yield* addPlugin()
|
||||
const provider = required(yield* catalog.provider.get(ProviderV2.ID.make("google-vertex")))
|
||||
expect(provider.request.body.project).toBe("config-project")
|
||||
expect(provider.request.body.location).toBe("us-central1")
|
||||
expect(provider.settings?.project).toBe("config-project")
|
||||
expect(provider.settings?.location).toBe("us-central1")
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -297,11 +295,8 @@ describe("GoogleVertexPlugin", () => {
|
|||
yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("gemini")),
|
||||
api: {
|
||||
id: ModelV2.ID.make("gemini"),
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/google-vertex",
|
||||
},
|
||||
modelID: ModelV2.ID.make("gemini"),
|
||||
package: "aisdk:@ai-sdk/google-vertex",
|
||||
}),
|
||||
package: "@ai-sdk/google-vertex",
|
||||
options: { name: "google-vertex" },
|
||||
|
|
@ -345,11 +340,8 @@ describe("GoogleVertexPlugin", () => {
|
|||
aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("gemini")),
|
||||
api: {
|
||||
id: ModelV2.ID.make("gemini"),
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
},
|
||||
modelID: ModelV2.ID.make("gemini"),
|
||||
package: "aisdk:@ai-sdk/openai-compatible",
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: { name: "google-vertex" },
|
||||
|
|
@ -376,7 +368,8 @@ describe("GoogleVertexPlugin", () => {
|
|||
yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make(" gemini-2.5-pro ")),
|
||||
api: { id: ModelV2.ID.make(" gemini-2.5-pro "), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make(" gemini-2.5-pro "),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
|
||||
options: {},
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ describe("GooglePlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom-google"), ModelV2.ID.make("gemini")),
|
||||
api: { id: ModelV2.ID.make("gemini"), type: "aisdk", package: "@ai-sdk/google" },
|
||||
modelID: ModelV2.ID.make("gemini"),
|
||||
package: "aisdk:@ai-sdk/google",
|
||||
}),
|
||||
package: "@ai-sdk/google",
|
||||
options: { name: "custom-google", apiKey: "test" },
|
||||
|
|
@ -45,7 +46,8 @@ describe("GooglePlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("google"), ModelV2.ID.make("gemini")),
|
||||
api: { id: ModelV2.ID.make("gemini"), type: "aisdk", package: "@ai-sdk/google" },
|
||||
modelID: ModelV2.ID.make("gemini"),
|
||||
package: "aisdk:@ai-sdk/google",
|
||||
}),
|
||||
package: "@ai-sdk/google-vertex",
|
||||
options: { name: "google" },
|
||||
|
|
@ -62,7 +64,8 @@ describe("GooglePlugin", () => {
|
|||
const sdkEvent = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom-google"), ModelV2.ID.make("alias")),
|
||||
api: { id: ModelV2.ID.make("gemini-api"), type: "aisdk", package: "@ai-sdk/google" },
|
||||
modelID: ModelV2.ID.make("gemini-api"),
|
||||
package: "aisdk:@ai-sdk/google",
|
||||
}),
|
||||
package: "@ai-sdk/google",
|
||||
options: { name: "custom-google", apiKey: "test" },
|
||||
|
|
@ -72,9 +75,29 @@ describe("GooglePlugin", () => {
|
|||
sdk: sdkEvent.sdk,
|
||||
options: sdkEvent.options,
|
||||
})
|
||||
const language = result.language ?? result.sdk.languageModel(result.model.api.id)
|
||||
const language = result.language ?? result.sdk.languageModel(result.model.modelID ?? result.model.id)
|
||||
expect(language.modelId).toBe("gemini-api")
|
||||
expect(language.provider).toBe("custom-google")
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("wraps AI SDK language models for the native runner", () =>
|
||||
Effect.gen(function* () {
|
||||
const aisdk = yield* AISDK.Service
|
||||
yield* addPlugin()
|
||||
|
||||
const resolved = yield* aisdk.model(
|
||||
ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom-google"), ModelV2.ID.make("alias")),
|
||||
modelID: ModelV2.ID.make("gemini-api"),
|
||||
package: "aisdk:@ai-sdk/google",
|
||||
settings: { apiKey: "test" },
|
||||
}),
|
||||
)
|
||||
|
||||
expect(String(resolved.id)).toBe("gemini-api")
|
||||
expect(String(resolved.provider)).toBe("custom-google")
|
||||
expect(resolved.route.id).toBe("ai-sdk:@ai-sdk/google")
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ describe("GroqPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("groq"), ModelV2.ID.make("llama")),
|
||||
api: { id: ModelV2.ID.make("llama"), type: "aisdk", package: "@ai-sdk/groq" },
|
||||
modelID: ModelV2.ID.make("llama"),
|
||||
package: "aisdk:@ai-sdk/groq",
|
||||
}),
|
||||
package: "@ai-sdk/groq",
|
||||
options: { name: "groq" },
|
||||
|
|
@ -45,7 +46,8 @@ describe("GroqPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("groq"), ModelV2.ID.make("llama")),
|
||||
api: { id: ModelV2.ID.make("llama"), type: "aisdk", package: "@ai-sdk/groq" },
|
||||
modelID: ModelV2.ID.make("llama"),
|
||||
package: "aisdk:@ai-sdk/groq",
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: { name: "groq" },
|
||||
|
|
@ -62,7 +64,8 @@ describe("GroqPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("groq"), ModelV2.ID.make("llama")),
|
||||
api: { id: ModelV2.ID.make("llama"), type: "aisdk", package: "@ai-sdk/groq" },
|
||||
modelID: ModelV2.ID.make("llama"),
|
||||
package: "aisdk:@ai-sdk/groq",
|
||||
}),
|
||||
package: "@ai-sdk/groq/compat",
|
||||
options: { name: "groq" },
|
||||
|
|
@ -79,7 +82,8 @@ describe("GroqPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom-groq"), ModelV2.ID.make("llama")),
|
||||
api: { id: ModelV2.ID.make("llama"), type: "aisdk", package: "@ai-sdk/groq" },
|
||||
modelID: ModelV2.ID.make("llama"),
|
||||
package: "aisdk:@ai-sdk/groq",
|
||||
}),
|
||||
package: "@ai-sdk/groq",
|
||||
options: { name: "custom-groq", apiKey: "test" },
|
||||
|
|
@ -93,7 +97,7 @@ describe("GroqPlugin", () => {
|
|||
}),
|
||||
)
|
||||
|
||||
it.effect("uses the default languageModel(api.id) behavior", () =>
|
||||
it.effect("uses the default languageModel(modelID) behavior", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const aisdk = yield* AISDK.Service
|
||||
|
|
@ -104,16 +108,13 @@ describe("GroqPlugin", () => {
|
|||
const result = yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("groq"), ModelV2.ID.make("alias")),
|
||||
api: {
|
||||
id: ModelV2.ID.make("llama-api"),
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/groq",
|
||||
},
|
||||
modelID: ModelV2.ID.make("llama-api"),
|
||||
package: "aisdk:@ai-sdk/groq",
|
||||
}),
|
||||
sdk,
|
||||
options: { name: "groq", apiKey: "test" },
|
||||
})
|
||||
const language = result.language ?? sdk.languageModel(result.model.api.id)
|
||||
const language = result.language ?? sdk.languageModel(result.model.modelID ?? result.model.id)
|
||||
expect(language.modelId).toBe("llama-api")
|
||||
expect(language.provider).toBe("groq.chat")
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -27,22 +27,19 @@ describe("KiloPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.make("kilo"), (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://api.kilo.ai/api/gateway",
|
||||
}
|
||||
provider.request = { headers: { Existing: "value" }, body: {} }
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
|
||||
provider.settings = { ...provider.settings, baseURL: "https://api.kilo.ai/api/gateway" }
|
||||
provider.headers = { Existing: "value" }
|
||||
})
|
||||
catalog.provider.update(ProviderV2.ID.openrouter, () => {})
|
||||
})
|
||||
yield* addPlugin()
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("kilo")))?.request.headers).toEqual({
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("kilo")))?.headers).toEqual({
|
||||
Existing: "value",
|
||||
"HTTP-Referer": "https://opencode.ai/",
|
||||
"X-Title": "opencode",
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.openrouter))?.request.headers).toEqual({})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.openrouter))?.headers).toBeUndefined()
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -51,24 +48,19 @@ describe("KiloPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.make("kilo"), (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://api.kilo.ai/api/gateway",
|
||||
}
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
|
||||
provider.settings = { ...provider.settings, baseURL: "https://api.kilo.ai/api/gateway" }
|
||||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("kilo")))?.request.headers).toEqual({
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("kilo")))?.headers).toEqual({
|
||||
"HTTP-Referer": "https://opencode.ai/",
|
||||
"X-Title": "opencode",
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("kilo")))?.request.headers).not.toHaveProperty(
|
||||
"http-referer",
|
||||
)
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("kilo")))?.request.headers).not.toHaveProperty("x-title")
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("kilo")))?.request.headers).not.toHaveProperty("X-Source")
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("kilo")))?.headers).not.toHaveProperty("http-referer")
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("kilo")))?.headers).not.toHaveProperty("x-title")
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("kilo")))?.headers).not.toHaveProperty("X-Source")
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -77,23 +69,20 @@ describe("KiloPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.make("kilo"), (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://api.kilo.ai/api/gateway",
|
||||
}
|
||||
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.api = { type: "aisdk", package: "kilo" }
|
||||
provider.package = ProviderV2.aisdk("kilo")
|
||||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("kilo")))?.request.headers).toEqual({
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("kilo")))?.headers).toEqual({
|
||||
"HTTP-Referer": "https://opencode.ai/",
|
||||
"X-Title": "opencode",
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("custom-kilo")))?.request.headers).toEqual({})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("custom-kilo")))?.headers).toBeUndefined()
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -34,23 +34,20 @@ describe("LLMGatewayPlugin", () => {
|
|||
})
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.make("llmgateway"), (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://api.llmgateway.io/v1",
|
||||
}
|
||||
provider.request = { headers: { Existing: "value" }, body: {} }
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
|
||||
provider.settings = { ...provider.settings, baseURL: "https://api.llmgateway.io/v1" }
|
||||
provider.headers = { Existing: "value" }
|
||||
})
|
||||
catalog.provider.update(ProviderV2.ID.openrouter, () => {})
|
||||
})
|
||||
yield* addPlugin()
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("llmgateway")))?.request.headers).toEqual({
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("llmgateway")))?.headers).toEqual({
|
||||
Existing: "value",
|
||||
"HTTP-Referer": "https://opencode.ai/",
|
||||
"X-Title": "opencode",
|
||||
"X-Source": "opencode",
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.openrouter))?.request.headers).toEqual({})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.openrouter))?.headers).toBeUndefined()
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -64,17 +61,14 @@ describe("LLMGatewayPlugin", () => {
|
|||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.make("llmgateway"), (provider) => {
|
||||
provider.disabled = true
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://api.llmgateway.io/v1",
|
||||
}
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
|
||||
provider.settings = { ...provider.settings, baseURL: "https://api.llmgateway.io/v1" }
|
||||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("llmgateway")))?.disabled).toBe(true)
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("llmgateway")))?.request.headers).toEqual({})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("llmgateway")))?.headers).toBeUndefined()
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ describe("MistralPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("mistral"), ModelV2.ID.make("mistral-large")),
|
||||
api: { id: ModelV2.ID.make("mistral-large"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("mistral-large"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/mistral",
|
||||
options: { name: "mistral" },
|
||||
|
|
@ -45,7 +46,8 @@ describe("MistralPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("mistral"), ModelV2.ID.make("mistral-large")),
|
||||
api: { id: ModelV2.ID.make("mistral-large"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("mistral-large"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: { name: "mistral" },
|
||||
|
|
@ -68,7 +70,8 @@ describe("MistralPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("mistral"), ModelV2.ID.make("mistral-large")),
|
||||
api: { id: ModelV2.ID.make("mistral-large"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("mistral-large"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/mistral",
|
||||
options: { name: "mistral" },
|
||||
|
|
@ -92,7 +95,8 @@ describe("MistralPlugin", () => {
|
|||
yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom-mistral"), ModelV2.ID.make("mistral-large")),
|
||||
api: { id: ModelV2.ID.make("mistral-large"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("mistral-large"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/mistral",
|
||||
options: { name: "custom-mistral" },
|
||||
|
|
@ -101,7 +105,7 @@ describe("MistralPlugin", () => {
|
|||
}),
|
||||
)
|
||||
|
||||
it.effect("leaves Mistral language selection on the default sdk.languageModel(api.id) path", () =>
|
||||
it.effect("leaves Mistral language selection on the default sdk.languageModel(modelID) path", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const aisdk = yield* AISDK.Service
|
||||
|
|
@ -116,12 +120,13 @@ describe("MistralPlugin", () => {
|
|||
const result = yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("mistral"), ModelV2.ID.make("alias")),
|
||||
api: { id: ModelV2.ID.make("mistral-large"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("mistral-large"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk,
|
||||
options: {},
|
||||
})
|
||||
const language = result.language ?? sdk.languageModel(result.model.api.id)
|
||||
const language = result.language ?? sdk.languageModel(result.model.modelID ?? result.model.id)
|
||||
expect(calls).toEqual(["languageModel:mistral-large"])
|
||||
expect(language).toBeDefined()
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -27,23 +27,20 @@ describe("NvidiaPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.make("nvidia"), (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://integrate.api.nvidia.com/v1",
|
||||
}
|
||||
provider.request = { headers: { Existing: "value" }, body: {} }
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
|
||||
provider.settings = { ...provider.settings, baseURL: "https://integrate.api.nvidia.com/v1" }
|
||||
provider.headers = { Existing: "value" }
|
||||
})
|
||||
catalog.provider.update(ProviderV2.ID.openrouter, () => {})
|
||||
})
|
||||
yield* addPlugin()
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("nvidia")))?.request.headers).toEqual({
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("nvidia")))?.headers).toEqual({
|
||||
Existing: "value",
|
||||
"HTTP-Referer": "https://opencode.ai/",
|
||||
"X-Title": "opencode",
|
||||
"X-BILLING-INVOKE-ORIGIN": "OpenCode",
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.openrouter))?.request.headers).toEqual({})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.openrouter))?.headers).toBeUndefined()
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -52,16 +49,13 @@ describe("NvidiaPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.make("nvidia"), (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://integrate.api.nvidia.com/v1",
|
||||
}
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
|
||||
provider.settings = { ...provider.settings, baseURL: "https://integrate.api.nvidia.com/v1" }
|
||||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("nvidia")))?.request.headers).toEqual({
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("nvidia")))?.headers).toEqual({
|
||||
"HTTP-Referer": "https://opencode.ai/",
|
||||
"X-Title": "opencode",
|
||||
"X-BILLING-INVOKE-ORIGIN": "OpenCode",
|
||||
|
|
@ -74,20 +68,14 @@ describe("NvidiaPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.make("nvidia"), (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://integrate.api.nvidia.com/v1",
|
||||
}
|
||||
provider.request = {
|
||||
headers: { "X-BILLING-INVOKE-ORIGIN": "CustomOrigin" },
|
||||
body: { baseURL: "https://integrate.api.nvidia.com/v1" },
|
||||
}
|
||||
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" }
|
||||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("nvidia")))?.request.headers).toEqual({
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("nvidia")))?.headers).toEqual({
|
||||
"HTTP-Referer": "https://opencode.ai/",
|
||||
"X-Title": "opencode",
|
||||
"X-BILLING-INVOKE-ORIGIN": "CustomOrigin",
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ describe("OpenAICompatiblePlugin", () => {
|
|||
const defaulted = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("model")),
|
||||
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("model"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: { name: "custom" },
|
||||
|
|
@ -35,7 +36,8 @@ describe("OpenAICompatiblePlugin", () => {
|
|||
const disabled = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("model")),
|
||||
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("model"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: { name: "custom", includeUsage: false },
|
||||
|
|
@ -53,7 +55,8 @@ describe("OpenAICompatiblePlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("model")),
|
||||
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("model"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "file:///tmp/@ai-sdk/openai-compatible-provider.js",
|
||||
options: { name: "custom" },
|
||||
|
|
@ -76,7 +79,8 @@ describe("OpenAICompatiblePlugin", () => {
|
|||
yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom-provider"), ModelV2.ID.make("model")),
|
||||
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("model"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: { name: "custom-provider", baseURL: "https://example.com/v1" },
|
||||
|
|
@ -96,7 +100,8 @@ describe("OpenAICompatiblePlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("model")),
|
||||
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("model"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: { name: "cloudflare-workers-ai" },
|
||||
|
|
|
|||
|
|
@ -67,7 +67,8 @@ describe("OpenAIPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom-openai"), ModelV2.ID.make("gpt-5")),
|
||||
api: { id: ModelV2.ID.make("gpt-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("gpt-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/openai",
|
||||
options: { name: "custom-openai", apiKey: "test" },
|
||||
|
|
@ -84,7 +85,8 @@ describe("OpenAIPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.openai, ModelV2.ID.make("gpt-5")),
|
||||
api: { id: ModelV2.ID.make("gpt-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("gpt-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: { name: "openai" },
|
||||
|
|
@ -102,7 +104,8 @@ describe("OpenAIPlugin", () => {
|
|||
const result = yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.openai, ModelV2.ID.make("alias")),
|
||||
api: { id: ModelV2.ID.make("gpt-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("gpt-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
@ -121,7 +124,8 @@ describe("OpenAIPlugin", () => {
|
|||
const result = yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.anthropic, ModelV2.ID.make("gpt-5")),
|
||||
api: { id: ModelV2.ID.make("gpt-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("gpt-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
@ -137,10 +141,11 @@ describe("OpenAIPlugin", () => {
|
|||
yield* catalog.transform((catalog) => {
|
||||
const item = ProviderV2.Info.make({
|
||||
...ProviderV2.Info.empty(ProviderV2.ID.openai),
|
||||
api: { type: "aisdk", package: "@ai-sdk/openai" },
|
||||
package: "aisdk:@ai-sdk/openai",
|
||||
})
|
||||
catalog.provider.update(item.id, (draft) => {
|
||||
draft.api = item.api
|
||||
draft.package = item.package
|
||||
draft.package = item.package
|
||||
})
|
||||
catalog.model.update(item.id, ModelV2.ID.make("gpt-5"), () => {})
|
||||
catalog.model.update(item.id, ModelV2.ID.make("gpt-5-chat-latest"), () => {})
|
||||
|
|
@ -159,10 +164,11 @@ describe("OpenAIPlugin", () => {
|
|||
yield* catalog.transform((catalog) => {
|
||||
const item = ProviderV2.Info.make({
|
||||
...ProviderV2.Info.empty(ProviderV2.ID.make("custom-openai")),
|
||||
api: { type: "aisdk", package: "test-provider" },
|
||||
package: "aisdk:test-provider",
|
||||
})
|
||||
catalog.provider.update(item.id, (draft) => {
|
||||
draft.api = item.api
|
||||
draft.package = item.package
|
||||
draft.package = item.package
|
||||
})
|
||||
catalog.model.update(item.id, ModelV2.ID.make("gpt-5-chat-latest"), () => {})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -162,13 +162,11 @@ describe("OpencodePlugin", () => {
|
|||
expect(provider).toMatchObject({
|
||||
name: "Remote",
|
||||
integrationID: "opencode",
|
||||
api: {
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: `${server.url.origin}/v1`,
|
||||
},
|
||||
package: "aisdk:@ai-sdk/openai-compatible",
|
||||
settings: { baseURL: `${server.url.origin}/v1` },
|
||||
})
|
||||
expect(provider.request).toEqual({ headers: { "x-org-id": "org" }, body: { custom: "value" } })
|
||||
expect(provider.headers).toEqual({ "x-org-id": "org" })
|
||||
expect(provider.settings).toMatchObject({ custom: "value" })
|
||||
expect(yield* (yield* Integration.Service).get(Integration.ID.make("remote"))).toBeUndefined()
|
||||
|
||||
const model = required(yield* catalog.model.get(ProviderV2.ID.make("remote"), ModelV2.ID.make("model")))
|
||||
|
|
@ -179,12 +177,16 @@ describe("OpencodePlugin", () => {
|
|||
cost: [{ input: 1, output: 2, cache: { read: 0.1, write: 0 } }],
|
||||
limit: { context: 1000, output: 100 },
|
||||
})
|
||||
expect(model.request.body).toEqual({ custom: "value", temperature: 0.5 })
|
||||
expect(model.settings).toEqual({
|
||||
baseURL: `${server.url.origin}/v1`,
|
||||
custom: "value",
|
||||
temperature: 0.5,
|
||||
})
|
||||
expect(model.variants).toEqual([
|
||||
{
|
||||
id: ModelV2.VariantID.make("high"),
|
||||
headers: {},
|
||||
body: { temperature: 0.2 },
|
||||
settings: { temperature: 0.2 },
|
||||
},
|
||||
])
|
||||
expect(
|
||||
|
|
@ -204,11 +206,12 @@ describe("OpencodePlugin", () => {
|
|||
yield* catalog.transform((catalog) => {
|
||||
const provider = ProviderV2.Info.make({
|
||||
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
|
||||
api: { type: "aisdk", package: "test-provider" },
|
||||
package: "aisdk:test-provider",
|
||||
})
|
||||
const model = ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(provider.id, ModelV2.ID.make("paid")),
|
||||
api: { id: ModelV2.ID.make("paid"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("paid"),
|
||||
package: "aisdk:test-provider",
|
||||
cost: cost(1),
|
||||
})
|
||||
catalog.provider.update(provider.id, () => {})
|
||||
|
|
@ -217,7 +220,7 @@ describe("OpencodePlugin", () => {
|
|||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).request.body.apiKey).toBe("public")
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).settings?.apiKey).toBe("public")
|
||||
expect(required(yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(false)
|
||||
}),
|
||||
),
|
||||
|
|
@ -230,11 +233,12 @@ describe("OpencodePlugin", () => {
|
|||
yield* catalog.transform((catalog) => {
|
||||
const provider = ProviderV2.Info.make({
|
||||
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
|
||||
api: { type: "aisdk", package: "test-provider" },
|
||||
package: "aisdk:test-provider",
|
||||
})
|
||||
const model = ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(provider.id, ModelV2.ID.make("free")),
|
||||
api: { id: ModelV2.ID.make("free"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("free"),
|
||||
package: "aisdk:test-provider",
|
||||
cost: cost(0),
|
||||
})
|
||||
catalog.provider.update(provider.id, () => {})
|
||||
|
|
@ -243,7 +247,7 @@ describe("OpencodePlugin", () => {
|
|||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).request.body.apiKey).toBe("public")
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).settings?.apiKey).toBe("public")
|
||||
expect(required(yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("free"))).enabled).toBe(true)
|
||||
}),
|
||||
),
|
||||
|
|
@ -256,11 +260,12 @@ describe("OpencodePlugin", () => {
|
|||
yield* catalog.transform((catalog) => {
|
||||
const provider = ProviderV2.Info.make({
|
||||
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
|
||||
api: { type: "aisdk", package: "test-provider" },
|
||||
package: "aisdk:test-provider",
|
||||
})
|
||||
const model = ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(provider.id, ModelV2.ID.make("output-only")),
|
||||
api: { id: ModelV2.ID.make("output-only"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("output-only"),
|
||||
package: "aisdk:test-provider",
|
||||
cost: cost(0, 1),
|
||||
})
|
||||
catalog.provider.update(provider.id, () => {})
|
||||
|
|
@ -269,7 +274,7 @@ describe("OpencodePlugin", () => {
|
|||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).request.body.apiKey).toBe("public")
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).settings?.apiKey).toBe("public")
|
||||
expect(required(yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("output-only"))).enabled).toBe(
|
||||
true,
|
||||
)
|
||||
|
|
@ -284,11 +289,12 @@ describe("OpencodePlugin", () => {
|
|||
yield* catalog.transform((catalog) => {
|
||||
const provider = ProviderV2.Info.make({
|
||||
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
|
||||
api: { type: "aisdk", package: "test-provider" },
|
||||
package: "aisdk:test-provider",
|
||||
})
|
||||
const model = ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(provider.id, ModelV2.ID.make("paid")),
|
||||
api: { id: ModelV2.ID.make("paid"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("paid"),
|
||||
package: "aisdk:test-provider",
|
||||
cost: cost(1),
|
||||
})
|
||||
catalog.provider.update(provider.id, () => {})
|
||||
|
|
@ -297,7 +303,7 @@ describe("OpencodePlugin", () => {
|
|||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).request.body.apiKey).toBeUndefined()
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).settings?.apiKey).toBeUndefined()
|
||||
expect(required(yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
||||
}),
|
||||
),
|
||||
|
|
@ -317,11 +323,12 @@ describe("OpencodePlugin", () => {
|
|||
yield* catalog.transform((catalog) => {
|
||||
const provider = ProviderV2.Info.make({
|
||||
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
|
||||
api: { type: "aisdk", package: "test-provider" },
|
||||
package: "aisdk:test-provider",
|
||||
})
|
||||
const model = ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(provider.id, ModelV2.ID.make("paid")),
|
||||
api: { id: ModelV2.ID.make("paid"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("paid"),
|
||||
package: "aisdk:test-provider",
|
||||
cost: cost(1),
|
||||
})
|
||||
catalog.provider.update(provider.id, () => {})
|
||||
|
|
@ -330,7 +337,7 @@ describe("OpencodePlugin", () => {
|
|||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).request.body.apiKey).toBeUndefined()
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).settings?.apiKey).toBeUndefined()
|
||||
expect(required(yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
||||
}),
|
||||
),
|
||||
|
|
@ -343,26 +350,26 @@ describe("OpencodePlugin", () => {
|
|||
yield* catalog.transform((catalog) => {
|
||||
const provider = ProviderV2.Info.make({
|
||||
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
|
||||
api: { type: "aisdk", package: "test-provider" },
|
||||
request: {
|
||||
headers: {},
|
||||
body: { apiKey: "configured" },
|
||||
},
|
||||
package: "aisdk:test-provider",
|
||||
headers: {},
|
||||
settings: { apiKey: "configured" },
|
||||
})
|
||||
const model = ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(provider.id, ModelV2.ID.make("paid")),
|
||||
api: { id: ModelV2.ID.make("paid"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("paid"),
|
||||
package: "aisdk:test-provider",
|
||||
cost: cost(1),
|
||||
})
|
||||
catalog.provider.update(provider.id, (draft) => {
|
||||
draft.request = provider.request
|
||||
draft.headers = provider.headers
|
||||
draft.settings = { apiKey: "configured" }
|
||||
})
|
||||
catalog.model.update(provider.id, model.id, (draft) => {
|
||||
draft.cost = [...model.cost]
|
||||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).request.body.apiKey).toBe("configured")
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).settings?.apiKey).toBe("configured")
|
||||
expect(required(yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
||||
}),
|
||||
),
|
||||
|
|
@ -375,11 +382,12 @@ describe("OpencodePlugin", () => {
|
|||
yield* catalog.transform((catalog) => {
|
||||
const provider = ProviderV2.Info.make({
|
||||
...ProviderV2.Info.empty(ProviderV2.ID.openai),
|
||||
api: { type: "aisdk", package: "test-provider" },
|
||||
package: "aisdk:test-provider",
|
||||
})
|
||||
const model = ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(provider.id, ModelV2.ID.make("paid")),
|
||||
api: { id: ModelV2.ID.make("paid"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("paid"),
|
||||
package: "aisdk:test-provider",
|
||||
cost: cost(1),
|
||||
})
|
||||
catalog.provider.update(provider.id, () => {})
|
||||
|
|
@ -388,7 +396,7 @@ describe("OpencodePlugin", () => {
|
|||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.openai)).request.body.apiKey).toBeUndefined()
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.openai)).settings?.apiKey).toBeUndefined()
|
||||
expect(required(yield* catalog.model.get(ProviderV2.ID.openai, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
||||
}),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -30,19 +30,20 @@ describe("OpenRouterPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.openrouter, (provider) => {
|
||||
provider.api = { type: "aisdk", package: "@openrouter/ai-sdk-provider" }
|
||||
provider.request = { headers: { Existing: "value" }, body: {} }
|
||||
provider.package = ProviderV2.aisdk("@openrouter/ai-sdk-provider")
|
||||
provider.headers = { Existing: "value" }
|
||||
provider.settings = {}
|
||||
})
|
||||
catalog.provider.update(ProviderV2.ID.make("nvidia"), () => {})
|
||||
})
|
||||
yield* addPlugin()
|
||||
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.openrouter))?.request.headers).toEqual({
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.openrouter))?.headers).toEqual({
|
||||
Existing: "value",
|
||||
"HTTP-Referer": "https://opencode.ai/",
|
||||
"X-Title": "opencode",
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("nvidia")))?.request.headers).toEqual({})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("nvidia")))?.headers).toBeUndefined()
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -55,7 +56,8 @@ describe("OpenRouterPlugin", () => {
|
|||
const ignored = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.openrouter, ModelV2.ID.make("openai/gpt-5")),
|
||||
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("openai/gpt-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: { name: "openrouter" },
|
||||
|
|
@ -65,7 +67,8 @@ describe("OpenRouterPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("openai/gpt-5")),
|
||||
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("openai/gpt-5"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@openrouter/ai-sdk-provider",
|
||||
options: { name: "custom" },
|
||||
|
|
@ -79,7 +82,7 @@ describe("OpenRouterPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.openrouter, (provider) => {
|
||||
provider.api = { type: "aisdk", 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"), () => {})
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ describe("PerplexityPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("perplexity"), ModelV2.ID.make("sonar")),
|
||||
api: { id: ModelV2.ID.make("sonar"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("sonar"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/perplexity",
|
||||
options: { name: "perplexity" },
|
||||
|
|
@ -58,7 +59,8 @@ describe("PerplexityPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("perplexity"), ModelV2.ID.make("sonar")),
|
||||
api: { id: ModelV2.ID.make("sonar"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("sonar"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/perplexity-compatible",
|
||||
options: { name: "perplexity" },
|
||||
|
|
@ -75,7 +77,8 @@ describe("PerplexityPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("perplexity"), ModelV2.ID.make("sonar")),
|
||||
api: { id: ModelV2.ID.make("sonar"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("sonar"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/perplexity",
|
||||
options: { name: "perplexity" },
|
||||
|
|
@ -92,7 +95,8 @@ describe("PerplexityPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom-perplexity"), ModelV2.ID.make("sonar")),
|
||||
api: { id: ModelV2.ID.make("sonar"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("sonar"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/perplexity",
|
||||
options: { name: "custom-perplexity" },
|
||||
|
|
@ -110,7 +114,8 @@ describe("PerplexityPlugin", () => {
|
|||
const result = yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("perplexity"), ModelV2.ID.make("alias")),
|
||||
api: { id: ModelV2.ID.make("sonar"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("sonar"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
|
|||
|
|
@ -49,7 +49,8 @@ function withEnv<A, E, R>(vars: Record<string, string | undefined>, effect: () =
|
|||
function model(providerID: string) {
|
||||
return ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make(providerID), ModelV2.ID.make("sap-model")),
|
||||
api: { id: ModelV2.ID.make("sap-model"), type: "aisdk", package: fixtureProvider },
|
||||
modelID: ModelV2.ID.make("sap-model"),
|
||||
package: ProviderV2.aisdk(fixtureProvider),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,8 @@ describe("SnowflakeCortexPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("openai"), ModelV2.ID.make("gpt-4")),
|
||||
api: { id: ModelV2.ID.make("gpt-4"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("gpt-4"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/openai",
|
||||
options: { name: "openai" },
|
||||
|
|
@ -75,7 +76,8 @@ describe("SnowflakeCortexPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("snowflake-cortex"), ModelV2.ID.make("claude-sonnet-4-6")),
|
||||
api: { id: ModelV2.ID.make("claude-sonnet-4-6"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("claude-sonnet-4-6"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: { name: "snowflake-cortex", baseURL: "https://test.snowflakecomputing.com/api/v2/cortex/v1" },
|
||||
|
|
@ -94,7 +96,8 @@ describe("SnowflakeCortexPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("snowflake-cortex"), ModelV2.ID.make("claude-sonnet-4-6")),
|
||||
api: { id: ModelV2.ID.make("claude-sonnet-4-6"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("claude-sonnet-4-6"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: {
|
||||
|
|
@ -117,7 +120,8 @@ describe("SnowflakeCortexPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("snowflake-cortex"), ModelV2.ID.make("claude-sonnet-4-6")),
|
||||
api: { id: ModelV2.ID.make("claude-sonnet-4-6"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("claude-sonnet-4-6"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: { name: "snowflake-cortex", baseURL: "https://test.snowflakecomputing.com/api/v2/cortex/v1" },
|
||||
|
|
@ -136,7 +140,8 @@ describe("SnowflakeCortexPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("snowflake-cortex"), ModelV2.ID.make("claude-sonnet-4-6")),
|
||||
api: { id: ModelV2.ID.make("claude-sonnet-4-6"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("claude-sonnet-4-6"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: {
|
||||
|
|
@ -159,7 +164,8 @@ describe("SnowflakeCortexPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("snowflake-cortex"), ModelV2.ID.make("claude-sonnet-4-6")),
|
||||
api: { id: ModelV2.ID.make("claude-sonnet-4-6"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("claude-sonnet-4-6"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: { name: "snowflake-cortex", baseURL: "https://test.snowflakecomputing.com/api/v2/cortex/v1" },
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ describe("TogetherAIPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("togetherai"), ModelV2.ID.make("model")),
|
||||
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("model"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/togetherai",
|
||||
options: { name: "togetherai" },
|
||||
|
|
@ -59,7 +60,8 @@ describe("TogetherAIPlugin", () => {
|
|||
const ignored = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("togetherai"), ModelV2.ID.make("model")),
|
||||
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("model"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "file:///tmp/@ai-sdk/togetherai-provider.js",
|
||||
options: { name: "togetherai" },
|
||||
|
|
@ -69,7 +71,8 @@ describe("TogetherAIPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("togetherai"), ModelV2.ID.make("model")),
|
||||
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("model"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/togetherai",
|
||||
options: { name: "togetherai" },
|
||||
|
|
@ -87,7 +90,8 @@ describe("TogetherAIPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom-togetherai"), ModelV2.ID.make("model")),
|
||||
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("model"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/togetherai",
|
||||
options: { name: "custom-togetherai" },
|
||||
|
|
@ -110,11 +114,8 @@ describe("TogetherAIPlugin", () => {
|
|||
ProviderV2.ID.make("togetherai"),
|
||||
ModelV2.ID.make("meta-llama/Llama-3.3-70B-Instruct-Turbo"),
|
||||
),
|
||||
api: {
|
||||
id: ModelV2.ID.make("meta-llama/Llama-3.3-70B-Instruct-Turbo"),
|
||||
type: "aisdk",
|
||||
package: "test-provider",
|
||||
},
|
||||
modelID: ModelV2.ID.make("meta-llama/Llama-3.3-70B-Instruct-Turbo"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
|
||||
options: {},
|
||||
|
|
@ -122,7 +123,9 @@ describe("TogetherAIPlugin", () => {
|
|||
|
||||
expect(result.language).toBeUndefined()
|
||||
expect(calls).toEqual([])
|
||||
expect(result.language ?? fakeSelectorSdk(calls).languageModel(result.model.api.id)).toBeDefined()
|
||||
expect(
|
||||
result.language ?? fakeSelectorSdk(calls).languageModel(result.model.modelID ?? result.model.id),
|
||||
).toBeDefined()
|
||||
expect(calls).toEqual(["languageModel:meta-llama/Llama-3.3-70B-Instruct-Turbo"])
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ describe("VenicePlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("venice"), ModelV2.ID.make("model")),
|
||||
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("model"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "venice-ai-sdk-provider",
|
||||
options: { name: "venice" },
|
||||
|
|
@ -58,7 +59,8 @@ describe("VenicePlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom-venice"), ModelV2.ID.make("model")),
|
||||
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("model"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "venice-ai-sdk-provider",
|
||||
options: { name: "custom-venice", apiKey: "test" },
|
||||
|
|
@ -76,7 +78,8 @@ describe("VenicePlugin", () => {
|
|||
const similar = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("venice"), ModelV2.ID.make("model")),
|
||||
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("model"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "file:///tmp/venice-ai-sdk-provider.js",
|
||||
options: { name: "venice" },
|
||||
|
|
@ -84,7 +87,8 @@ describe("VenicePlugin", () => {
|
|||
const other = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("venice"), ModelV2.ID.make("model")),
|
||||
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("model"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: { name: "venice" },
|
||||
|
|
@ -103,7 +107,8 @@ describe("VenicePlugin", () => {
|
|||
const result = yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("venice"), ModelV2.ID.make("alias")),
|
||||
api: { id: ModelV2.ID.make("alias"), type: "aisdk", package: "test-provider" },
|
||||
modelID: ModelV2.ID.make("alias"),
|
||||
package: "aisdk:test-provider",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
|
|||
|
|
@ -25,12 +25,12 @@ describe("VercelPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.make("vercel"), (provider) => {
|
||||
provider.api = { type: "aisdk", package: "@ai-sdk/vercel" }
|
||||
provider.request.headers.Existing = "1"
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/vercel")
|
||||
provider.headers = { ...provider.headers, Existing: "1" }
|
||||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("vercel")))?.request.headers).toEqual({
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("vercel")))?.headers).toEqual({
|
||||
Existing: "1",
|
||||
"http-referer": "https://opencode.ai/",
|
||||
"x-title": "opencode",
|
||||
|
|
@ -43,14 +43,12 @@ describe("VercelPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.make("vercel"), (provider) => {
|
||||
provider.api = { type: "aisdk", package: "@ai-sdk/vercel" }
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/vercel")
|
||||
}),
|
||||
)
|
||||
yield* addPlugin()
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("vercel")))?.request.headers).not.toHaveProperty(
|
||||
"HTTP-Referer",
|
||||
)
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("vercel")))?.request.headers).not.toHaveProperty("X-Title")
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("vercel")))?.headers).not.toHaveProperty("HTTP-Referer")
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("vercel")))?.headers).not.toHaveProperty("X-Title")
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -62,7 +60,8 @@ describe("VercelPlugin", () => {
|
|||
const event = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom-vercel"), ModelV2.ID.make("v0-1.0-md")),
|
||||
api: { id: ModelV2.ID.make("v0-1.0-md"), type: "aisdk", package: "@ai-sdk/vercel" },
|
||||
modelID: ModelV2.ID.make("v0-1.0-md"),
|
||||
package: "aisdk:@ai-sdk/vercel",
|
||||
}),
|
||||
package: "@ai-sdk/vercel",
|
||||
options: { name: "custom-vercel" },
|
||||
|
|
@ -77,7 +76,7 @@ describe("VercelPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) => catalog.provider.update(ProviderV2.ID.make("gateway"), () => {}))
|
||||
yield* addPlugin()
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("gateway")))?.request.headers).toEqual({})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("gateway")))?.headers).toBeUndefined()
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -42,7 +42,8 @@ describe("XAIPlugin", () => {
|
|||
const ignored = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("xai"), ModelV2.ID.make("grok-4")),
|
||||
api: { id: ModelV2.ID.make("grok-4"), type: "aisdk", package: "@ai-sdk/xai" },
|
||||
modelID: ModelV2.ID.make("grok-4"),
|
||||
package: "aisdk:@ai-sdk/xai",
|
||||
}),
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
options: {},
|
||||
|
|
@ -51,7 +52,8 @@ describe("XAIPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("xai"), ModelV2.ID.make("grok-4")),
|
||||
api: { id: ModelV2.ID.make("grok-4"), type: "aisdk", package: "@ai-sdk/xai" },
|
||||
modelID: ModelV2.ID.make("grok-4"),
|
||||
package: "aisdk:@ai-sdk/xai",
|
||||
}),
|
||||
package: "@ai-sdk/xai",
|
||||
options: {},
|
||||
|
|
@ -71,7 +73,8 @@ describe("XAIPlugin", () => {
|
|||
const result = yield* aisdk.runSDK({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("custom-xai"), ModelV2.ID.make("grok-4")),
|
||||
api: { id: ModelV2.ID.make("grok-4"), type: "aisdk", package: "@ai-sdk/xai" },
|
||||
modelID: ModelV2.ID.make("grok-4"),
|
||||
package: "aisdk:@ai-sdk/xai",
|
||||
}),
|
||||
package: "@ai-sdk/xai",
|
||||
options: {},
|
||||
|
|
@ -81,7 +84,7 @@ describe("XAIPlugin", () => {
|
|||
}),
|
||||
)
|
||||
|
||||
it.effect("uses responses with the model api.id for xAI language models", () =>
|
||||
it.effect("uses responses with the model modelID for xAI language models", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const aisdk = yield* AISDK.Service
|
||||
|
|
@ -91,7 +94,8 @@ describe("XAIPlugin", () => {
|
|||
const result = yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.make("xai"), ModelV2.ID.make("alias")),
|
||||
api: { id: ModelV2.ID.make("grok-4"), type: "aisdk", package: "@ai-sdk/xai" },
|
||||
modelID: ModelV2.ID.make("grok-4"),
|
||||
package: "aisdk:@ai-sdk/xai",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
@ -112,7 +116,8 @@ describe("XAIPlugin", () => {
|
|||
const result = yield* aisdk.runLanguage({
|
||||
model: ModelV2.Info.make({
|
||||
...ModelV2.Info.empty(ProviderV2.ID.openai, ModelV2.ID.make("grok-4")),
|
||||
api: { id: ModelV2.ID.make("grok-4"), type: "aisdk", package: "@ai-sdk/xai" },
|
||||
modelID: ModelV2.ID.make("grok-4"),
|
||||
package: "aisdk:@ai-sdk/xai",
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
|
|||
|
|
@ -32,17 +32,14 @@ describe("ZenmuxPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.make("zenmux"), (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://zenmux.ai/api/v1",
|
||||
}
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
|
||||
provider.settings = { ...provider.settings, baseURL: "https://zenmux.ai/api/v1" }
|
||||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
const result = required(yield* catalog.provider.get(ProviderV2.ID.make("zenmux")))
|
||||
expect(result.request.headers).toEqual({ "HTTP-Referer": "https://opencode.ai/", "X-Title": "opencode" })
|
||||
expect(Object.keys(result.request.headers).sort()).toEqual(["HTTP-Referer", "X-Title"])
|
||||
expect(result.headers).toEqual({ "HTTP-Referer": "https://opencode.ai/", "X-Title": "opencode" })
|
||||
expect(Object.keys(required(result.headers)).sort()).toEqual(["HTTP-Referer", "X-Title"])
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -51,17 +48,14 @@ describe("ZenmuxPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.make("zenmux"), (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://zenmux.ai/api/v1",
|
||||
}
|
||||
provider.request.headers.Existing = "value"
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
|
||||
provider.settings = { ...provider.settings, baseURL: "https://zenmux.ai/api/v1" }
|
||||
provider.headers = { ...provider.headers, Existing: "value" }
|
||||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("zenmux"))).request.headers).toEqual({
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("zenmux"))).headers).toEqual({
|
||||
Existing: "value",
|
||||
"HTTP-Referer": "https://opencode.ai/",
|
||||
"X-Title": "opencode",
|
||||
|
|
@ -74,17 +68,14 @@ describe("ZenmuxPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.make("zenmux"), (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://zenmux.ai/api/v1",
|
||||
}
|
||||
provider.request.headers = { "HTTP-Referer": "https://example.com/", "X-Title": "custom-title" }
|
||||
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" }
|
||||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("zenmux"))).request.headers).toEqual({
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("zenmux"))).headers).toEqual({
|
||||
"HTTP-Referer": "https://example.com/",
|
||||
"X-Title": "custom-title",
|
||||
})
|
||||
|
|
@ -96,12 +87,12 @@ describe("ZenmuxPlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.openrouter, (provider) => {
|
||||
provider.request.headers = { "HTTP-Referer": "https://example.com/", "X-Title": "custom-title" }
|
||||
provider.headers = { "HTTP-Referer": "https://example.com/", "X-Title": "custom-title" }
|
||||
})
|
||||
})
|
||||
yield* addPlugin()
|
||||
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.openrouter)).request.headers).toEqual({
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.openrouter)).headers).toEqual({
|
||||
"HTTP-Referer": "https://example.com/",
|
||||
"X-Title": "custom-title",
|
||||
})
|
||||
|
|
|
|||
|
|
@ -24,21 +24,18 @@ describe("VariantPlugin", () => {
|
|||
const service = yield* Catalog.Service
|
||||
yield* service.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.opencode, (provider) => {
|
||||
provider.api = { type: "aisdk", package: "@ai-sdk/openai-compatible" }
|
||||
provider.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
|
||||
})
|
||||
catalog.model.update(ProviderV2.ID.opencode, ModelV2.ID.make("glm-5.2"), (model) => {
|
||||
model.api = {
|
||||
id: ModelV2.ID.make("glm-5.2"),
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
}
|
||||
model.modelID = ModelV2.ID.make("glm-5.2")
|
||||
model.package = ProviderV2.aisdk("@ai-sdk/openai-compatible")
|
||||
})
|
||||
})
|
||||
yield* VariantPlugin.Plugin.effect(host({ catalog: catalogHost(service) }))
|
||||
|
||||
expect((yield* service.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("glm-5.2")))?.variants).toEqual([
|
||||
expect.objectContaining({ id: "high", body: { reasoning_effort: "high" } }),
|
||||
expect.objectContaining({ id: "max", body: { reasoning_effort: "max" } }),
|
||||
expect.objectContaining({ id: "high", settings: { reasoningEffort: "high" } }),
|
||||
expect.objectContaining({ id: "max", settings: { reasoningEffort: "max" } }),
|
||||
])
|
||||
}),
|
||||
)
|
||||
|
|
@ -48,19 +45,16 @@ describe("VariantPlugin", () => {
|
|||
const service = yield* Catalog.Service
|
||||
yield* service.transform((catalog) => {
|
||||
catalog.model.update(ProviderV2.ID.opencode, ModelV2.ID.make("glm-5.2"), (model) => {
|
||||
model.api = {
|
||||
id: ModelV2.ID.make("glm-5.2"),
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
}
|
||||
model.variants = [{ id: ModelV2.VariantID.make("high"), headers: { custom: "true" }, body: {} }]
|
||||
model.modelID = ModelV2.ID.make("glm-5.2")
|
||||
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) }))
|
||||
|
||||
expect((yield* service.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("glm-5.2")))?.variants).toEqual([
|
||||
expect.objectContaining({ id: "high", headers: { custom: "true" } }),
|
||||
expect.objectContaining({ id: "max", body: { reasoning_effort: "max" } }),
|
||||
expect.objectContaining({ id: "max", settings: { reasoningEffort: "max" } }),
|
||||
])
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { describe, expect } from "bun:test"
|
||||
import { LLM } from "@opencode-ai/llm"
|
||||
import { OpenAI } from "@opencode-ai/llm/providers"
|
||||
import { LLMClient } from "@opencode-ai/llm/route"
|
||||
import { DateTime, Effect } from "effect"
|
||||
import { Headers } from "effect/unstable/http"
|
||||
import { Credential } from "@opencode-ai/core/credential"
|
||||
import { Integration } from "@opencode-ai/core/integration"
|
||||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { ProjectV2 } from "@opencode-ai/core/project"
|
||||
|
|
@ -13,26 +13,22 @@ import { SessionV2 } from "@opencode-ai/core/session"
|
|||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||
import { it } from "./lib/effect"
|
||||
|
||||
type Api =
|
||||
| {
|
||||
readonly type: "aisdk"
|
||||
readonly package: string
|
||||
readonly url?: string
|
||||
readonly settings?: Record<string, unknown>
|
||||
}
|
||||
| { readonly type: "native"; readonly url?: string; readonly settings: Record<string, unknown> }
|
||||
type Transport = {
|
||||
readonly package: string
|
||||
readonly settings?: Record<string, unknown>
|
||||
}
|
||||
|
||||
const model = (api: Api, variants: ModelV2.Info["variants"] = []) =>
|
||||
const model = (transport: Transport, variants?: NonNullable<ModelV2.Info["variants"]>) =>
|
||||
ModelV2.Info.make({
|
||||
id: ModelV2.ID.make("test-model"),
|
||||
modelID: ModelV2.ID.make("api-test-model"),
|
||||
providerID: ProviderV2.ID.make("test-provider"),
|
||||
name: "Test model",
|
||||
api: { id: ModelV2.ID.make("api-test-model"), ...api },
|
||||
...transport,
|
||||
settings: { apiKey: "secret", ...transport.settings },
|
||||
capabilities: { tools: true, input: ["text"], output: ["text"] },
|
||||
request: {
|
||||
headers: { "x-test": "header" },
|
||||
body: { apiKey: "secret", custom_extension: { enabled: true } },
|
||||
},
|
||||
headers: { "x-test": "header" },
|
||||
body: { custom_extension: { enabled: true } },
|
||||
variants,
|
||||
time: { released: 0 },
|
||||
cost: [],
|
||||
|
|
@ -41,11 +37,49 @@ const model = (api: Api, variants: ModelV2.Info["variants"] = []) =>
|
|||
limit: { context: 100, output: 20 },
|
||||
})
|
||||
|
||||
const aisdkDependencies = {
|
||||
loadAISDK: (input: ModelV2.Info) =>
|
||||
Effect.succeed(
|
||||
OpenAI.model(input.modelID ?? input.id, {
|
||||
...input.settings,
|
||||
headers: input.headers,
|
||||
body: input.body,
|
||||
limits: { context: input.limit.context, output: input.limit.output },
|
||||
}),
|
||||
),
|
||||
}
|
||||
|
||||
describe("SessionRunnerModel", () => {
|
||||
it.effect("maps catalog OpenAI AI SDK models into native Responses routes", () =>
|
||||
it.effect("constructs native provider package models mechanically", () =>
|
||||
Effect.gen(function* () {
|
||||
const resolved = yield* SessionRunnerModel.fromCatalogModel(
|
||||
model({ type: "aisdk", package: "@ai-sdk/openai", url: "https://openai.example/v1" }),
|
||||
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 native provider packages into Responses routes", () =>
|
||||
Effect.gen(function* () {
|
||||
const resolved = yield* SessionRunnerModel.fromCatalogModel(
|
||||
model({ package: "@opencode-ai/llm/providers/openai", settings: { baseURL: "https://openai.example/v1" } }),
|
||||
)
|
||||
|
||||
expect(resolved).toMatchObject({ id: "api-test-model", provider: "test-provider" })
|
||||
|
|
@ -64,7 +98,7 @@ describe("SessionRunnerModel", () => {
|
|||
it.effect("keeps catalog apiKey credentials out of provider JSON", () =>
|
||||
Effect.gen(function* () {
|
||||
const resolved = yield* SessionRunnerModel.fromCatalogModel(
|
||||
model({ type: "aisdk", package: "@ai-sdk/openai", url: "https://openai.example/v1" }),
|
||||
model({ package: "@opencode-ai/llm/providers/openai", settings: { baseURL: "https://openai.example/v1" } }),
|
||||
)
|
||||
const prepared = yield* LLMClient.prepare(LLM.request({ model: resolved, prompt: "Hello" }))
|
||||
|
||||
|
|
@ -78,12 +112,15 @@ describe("SessionRunnerModel", () => {
|
|||
const resolved = yield* SessionRunnerModel.fromCatalogModel(
|
||||
ModelV2.Info.make({
|
||||
...model({
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://compatible.example/v1",
|
||||
settings: { apiKey: "settings-secret", compatibility: "strict" },
|
||||
package: "@opencode-ai/llm/providers/openai-compatible",
|
||||
settings: {
|
||||
apiKey: "settings-secret",
|
||||
baseURL: "https://compatible.example/v1",
|
||||
compatibility: "strict",
|
||||
},
|
||||
}),
|
||||
request: { headers: {}, body: {} },
|
||||
headers: {},
|
||||
body: {},
|
||||
}),
|
||||
)
|
||||
const request = LLM.request({ model: resolved, prompt: "Hello" })
|
||||
|
|
@ -102,7 +139,7 @@ describe("SessionRunnerModel", () => {
|
|||
|
||||
it.effect("overlays selected OpenAI Session variant bodies", () =>
|
||||
Effect.gen(function* () {
|
||||
const catalog = model({ type: "aisdk", package: "@ai-sdk/openai", url: "https://openai.example/v1" }, [
|
||||
const catalog = model({ package: "@opencode-ai/llm/providers/openai", settings: { baseURL: "https://openai.example/v1" } }, [
|
||||
{
|
||||
id: ModelV2.VariantID.make("high"),
|
||||
headers: { "x-variant": "high" },
|
||||
|
|
@ -145,7 +182,10 @@ describe("SessionRunnerModel", () => {
|
|||
it.effect("overlays selected OpenAI-compatible Session variant bodies", () =>
|
||||
Effect.gen(function* () {
|
||||
const catalog = model(
|
||||
{ type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://compatible.example/v1" },
|
||||
{
|
||||
package: "@opencode-ai/llm/providers/openai-compatible",
|
||||
settings: { baseURL: "https://compatible.example/v1" },
|
||||
},
|
||||
[
|
||||
{
|
||||
id: ModelV2.VariantID.make("high"),
|
||||
|
|
@ -177,7 +217,7 @@ describe("SessionRunnerModel", () => {
|
|||
|
||||
it.effect("rejects an explicit unavailable Session variant during model resolution", () =>
|
||||
Effect.gen(function* () {
|
||||
const catalog = model({ type: "aisdk", package: "@ai-sdk/openai", url: "https://openai.example/v1" })
|
||||
const catalog = model({ package: "@opencode-ai/llm/providers/openai", settings: { baseURL: "https://openai.example/v1" } })
|
||||
const session = SessionV2.Info.make({
|
||||
id: SessionV2.ID.make("ses_model_variant_unavailable"),
|
||||
projectID: ProjectV2.ID.global,
|
||||
|
|
@ -207,13 +247,16 @@ describe("SessionRunnerModel", () => {
|
|||
|
||||
it.effect("overlays selected Anthropic Session variant bodies", () =>
|
||||
Effect.gen(function* () {
|
||||
const catalog = model({ type: "aisdk", package: "@ai-sdk/anthropic", url: "https://anthropic.example/v1" }, [
|
||||
{
|
||||
id: ModelV2.VariantID.make("high"),
|
||||
headers: {},
|
||||
body: { thinking: { type: "enabled", budget_tokens: 12000 } },
|
||||
},
|
||||
])
|
||||
const catalog = model(
|
||||
{ package: "@opencode-ai/llm/providers/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,
|
||||
|
|
@ -234,10 +277,10 @@ describe("SessionRunnerModel", () => {
|
|||
}),
|
||||
)
|
||||
|
||||
it.effect("maps catalog Anthropic AI SDK models into native routes", () =>
|
||||
it.effect("maps catalog Anthropic native provider packages into native routes", () =>
|
||||
Effect.gen(function* () {
|
||||
const resolved = yield* SessionRunnerModel.fromCatalogModel(
|
||||
model({ type: "aisdk", package: "@ai-sdk/anthropic", url: "https://anthropic.example/v1" }),
|
||||
model({ package: "@opencode-ai/llm/providers/anthropic", settings: { baseURL: "https://anthropic.example/v1" } }),
|
||||
)
|
||||
|
||||
expect(resolved.route).toMatchObject({
|
||||
|
|
@ -251,8 +294,9 @@ describe("SessionRunnerModel", () => {
|
|||
Effect.gen(function* () {
|
||||
const resolved = yield* SessionRunnerModel.fromCatalogModel(
|
||||
ModelV2.Info.make({
|
||||
...model({ type: "aisdk", package: "@ai-sdk/openai", url: "https://openai.example/v1" }),
|
||||
request: { headers: {}, body: {} },
|
||||
...model({ package: "@opencode-ai/llm/providers/openai", settings: { baseURL: "https://openai.example/v1" } }),
|
||||
headers: {},
|
||||
body: {},
|
||||
}),
|
||||
Credential.Key.make({ type: "key", key: "secret" }),
|
||||
)
|
||||
|
|
@ -274,8 +318,10 @@ describe("SessionRunnerModel", () => {
|
|||
const credential = Credential.Key.make({ type: "key", key: "stored-secret", metadata: { tenant: "work" } })
|
||||
const resolved = yield* SessionRunnerModel.fromCatalogModel(
|
||||
ModelV2.Info.make({
|
||||
...model({ type: "aisdk", package: "@ai-sdk/openai", url: "https://openai.example/v1" }),
|
||||
request: { headers: {}, body: { apiKey: "configured-secret" } },
|
||||
...model({ package: "@opencode-ai/llm/providers/openai", settings: { baseURL: "https://openai.example/v1" } }),
|
||||
settings: { apiKey: "configured-secret", baseURL: "https://openai.example/v1" },
|
||||
headers: {},
|
||||
body: {},
|
||||
}),
|
||||
credential,
|
||||
)
|
||||
|
|
@ -288,60 +334,31 @@ describe("SessionRunnerModel", () => {
|
|||
})
|
||||
|
||||
expect(headers.authorization).toBe("Bearer stored-secret")
|
||||
expect(resolved.route.defaults.http?.body).toEqual({ tenant: "work" })
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("does not project OAuth account metadata into the request body", () =>
|
||||
Effect.gen(function* () {
|
||||
const resolved = yield* SessionRunnerModel.fromCatalogModel(
|
||||
ModelV2.Info.make({
|
||||
...model({ type: "aisdk", package: "@ai-sdk/openai", url: "https://openai.example/v1" }),
|
||||
request: { headers: {}, body: {} },
|
||||
}),
|
||||
Credential.OAuth.make({
|
||||
type: "oauth",
|
||||
methodID: Integration.MethodID.make("device"),
|
||||
access: "secret",
|
||||
refresh: "refresh",
|
||||
expires: Date.now() + 60_000,
|
||||
metadata: { server: "https://console.example", orgID: "org_123" },
|
||||
}),
|
||||
)
|
||||
|
||||
expect(resolved.route.defaults.http?.body).toEqual({})
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("rejects catalog APIs without a native route", () =>
|
||||
it.effect("delegates aisdk-prefixed packages to the compatibility resolver", () =>
|
||||
Effect.gen(function* () {
|
||||
const failure = yield* SessionRunnerModel.fromCatalogModel(
|
||||
model({ type: "aisdk", package: "@ai-sdk/google", url: "https://google.example/v1" }),
|
||||
).pipe(Effect.flip)
|
||||
const resolved = yield* SessionRunnerModel.fromCatalogModel(
|
||||
model({ package: "aisdk:@ai-sdk/google", settings: { baseURL: "https://google.example/v1" } }),
|
||||
undefined,
|
||||
aisdkDependencies,
|
||||
)
|
||||
|
||||
expect(failure).toMatchObject({
|
||||
_tag: "SessionRunnerModel.UnsupportedApiError",
|
||||
providerID: "test-provider",
|
||||
modelID: "test-model",
|
||||
api: "aisdk:@ai-sdk/google",
|
||||
})
|
||||
expect(failure.message).toBe("Unsupported API for test-provider/test-model: aisdk:@ai-sdk/google")
|
||||
expect(resolved.route.id).toBe("openai-responses")
|
||||
expect(resolved.route.endpoint).toMatchObject({ baseURL: "https://google.example/v1" })
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("reports whether a catalog model has a supported native route", () =>
|
||||
it.effect("reports whether a catalog model has a supported package", () =>
|
||||
Effect.sync(() => {
|
||||
expect(
|
||||
SessionRunnerModel.supported(
|
||||
model({ type: "aisdk", package: "@ai-sdk/openai", url: "https://openai.example/v1" }),
|
||||
model({ package: "aisdk:@ai-sdk/google", settings: { baseURL: "https://google.example/v1" } }),
|
||||
),
|
||||
).toBe(true)
|
||||
expect(
|
||||
SessionRunnerModel.supported(
|
||||
model({ type: "aisdk", package: "@ai-sdk/google", url: "https://google.example/v1" }),
|
||||
),
|
||||
).toBe(false)
|
||||
expect(SessionRunnerModel.supported(model({ type: "native", settings: {} }))).toBe(false)
|
||||
expect(SessionRunnerModel.supported(model({ package: "native-provider-package" }))).toBe(true)
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -116,12 +116,8 @@ test("Core reuses the canonical shared schemas", async () => {
|
|||
[ModelV2.Family, Model.Family],
|
||||
[ModelV2.Capabilities, Model.Capabilities],
|
||||
[ModelV2.Cost, Model.Cost],
|
||||
[ModelV2.Api, Model.Api],
|
||||
[ModelV2.Info, Model.Info],
|
||||
[ProviderV2.ID, Provider.ID],
|
||||
[ProviderV2.AISDK, Provider.AISDK],
|
||||
[ProviderV2.Native, Provider.Native],
|
||||
[ProviderV2.Api, Provider.Api],
|
||||
[ProviderV2.Request, Provider.Request],
|
||||
[ProviderV2.Info, Provider.Info],
|
||||
[corePermission.Effect, Permission.Effect],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue