feat(plugin): add v2 effect host (#33111)
This commit is contained in:
parent
7a9337da8a
commit
c780d7cee7
139 changed files with 3740 additions and 1943 deletions
|
|
@ -6,6 +6,7 @@ import { CommandPlugin } from "@opencode-ai/core/plugin/command"
|
|||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||
import { location } from "../fixture/location"
|
||||
import { testEffect } from "../lib/effect"
|
||||
import { host } from "./host"
|
||||
|
||||
const directory = AbsolutePath.make("/repo/packages/app")
|
||||
const project = AbsolutePath.make("/repo")
|
||||
|
|
@ -21,12 +22,11 @@ describe("CommandPlugin.Plugin", () => {
|
|||
it.effect("registers built-in init and review commands", () =>
|
||||
Effect.gen(function* () {
|
||||
const command = yield* CommandV2.Service
|
||||
yield* CommandPlugin.Plugin.effect.pipe(
|
||||
Effect.provideService(CommandV2.Service, command),
|
||||
Effect.provideService(
|
||||
Location.Service,
|
||||
Location.Service.of(location({ directory }, { projectDirectory: project })),
|
||||
),
|
||||
yield* CommandPlugin.Plugin.effect(
|
||||
host({
|
||||
command,
|
||||
location: location({ directory }, { projectDirectory: project }),
|
||||
}),
|
||||
)
|
||||
|
||||
expect(yield* command.get("init")).toMatchObject({
|
||||
|
|
|
|||
317
packages/core/test/plugin/host.ts
Normal file
317
packages/core/test/plugin/host.ts
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
import type { AISDKHooks, PluginHost } from "@opencode-ai/plugin/v2/effect"
|
||||
import { AgentV2 } from "@opencode-ai/core/agent"
|
||||
import { Catalog } from "@opencode-ai/core/catalog"
|
||||
import { Integration } from "@opencode-ai/core/integration"
|
||||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import type { IntegrationEnvMethod, IntegrationKeyMethod, IntegrationOAuthMethod } from "@opencode-ai/sdk/v2/types"
|
||||
import { Effect, Stream } from "effect"
|
||||
|
||||
export function host(overrides: Partial<PluginHost> = {}): PluginHost {
|
||||
return {
|
||||
aisdk: {
|
||||
hook: () => Effect.die("unused aisdk.hook"),
|
||||
},
|
||||
agent: {
|
||||
get: () => Effect.die("unused agent.get"),
|
||||
default: () => Effect.die("unused agent.default"),
|
||||
list: () => Effect.die("unused agent.list"),
|
||||
rebuild: () => Effect.die("unused agent.rebuild"),
|
||||
transform: () => Effect.die("unused agent.transform"),
|
||||
},
|
||||
catalog: {
|
||||
provider: {
|
||||
get: () => Effect.die("unused catalog.provider.get"),
|
||||
list: () => Effect.die("unused catalog.provider.list"),
|
||||
available: () => Effect.die("unused catalog.provider.available"),
|
||||
},
|
||||
model: {
|
||||
get: () => Effect.die("unused catalog.model.get"),
|
||||
list: () => Effect.die("unused catalog.model.list"),
|
||||
available: () => Effect.die("unused catalog.model.available"),
|
||||
default: () => Effect.die("unused catalog.model.default"),
|
||||
small: () => Effect.die("unused catalog.model.small"),
|
||||
},
|
||||
rebuild: () => Effect.die("unused catalog.rebuild"),
|
||||
transform: () => Effect.die("unused catalog.transform"),
|
||||
},
|
||||
command: {
|
||||
get: () => Effect.die("unused command.get"),
|
||||
list: () => Effect.die("unused command.list"),
|
||||
rebuild: () => Effect.die("unused command.rebuild"),
|
||||
transform: () => Effect.die("unused command.transform"),
|
||||
},
|
||||
event: {
|
||||
subscribe: () => Stream.die("unused event.subscribe"),
|
||||
},
|
||||
filesystem: {
|
||||
read: () => Effect.die("unused filesystem.read"),
|
||||
list: () => Effect.die("unused filesystem.list"),
|
||||
find: () => Effect.die("unused filesystem.find"),
|
||||
glob: () => Effect.die("unused filesystem.glob"),
|
||||
},
|
||||
integration: {
|
||||
get: () => Effect.die("unused integration.get"),
|
||||
list: () => Effect.die("unused integration.list"),
|
||||
rebuild: () => Effect.die("unused integration.rebuild"),
|
||||
transform: () => Effect.die("unused integration.transform"),
|
||||
},
|
||||
location: {
|
||||
directory: "/unused/location",
|
||||
project: { directory: "/unused/project" },
|
||||
},
|
||||
npm: {
|
||||
add: () => Effect.die("unused npm.add"),
|
||||
},
|
||||
path: {
|
||||
home: "/unused/home",
|
||||
data: "/unused/data",
|
||||
cache: "/unused/cache",
|
||||
config: "/unused/config",
|
||||
state: "/unused/state",
|
||||
temp: "/unused/temp",
|
||||
},
|
||||
reference: {
|
||||
list: () => Effect.die("unused reference.list"),
|
||||
rebuild: () => Effect.die("unused reference.rebuild"),
|
||||
transform: () => Effect.die("unused reference.transform"),
|
||||
},
|
||||
skill: {
|
||||
sources: () => Effect.die("unused skill.sources"),
|
||||
list: () => Effect.die("unused skill.list"),
|
||||
rebuild: () => Effect.die("unused skill.rebuild"),
|
||||
transform: () => Effect.die("unused skill.transform"),
|
||||
},
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
export function aisdkHost(plugin: PluginV2.Interface): PluginHost["aisdk"] {
|
||||
return {
|
||||
hook: (name, callback) => {
|
||||
if (name === "sdk") {
|
||||
const run = callback as AISDKHooks["sdk"]
|
||||
return plugin.hook("aisdk.sdk", (event) => {
|
||||
const output = { ...event }
|
||||
const result = run(output)
|
||||
return Effect.suspend(() => (Effect.isEffect(result) ? result : Effect.void)).pipe(
|
||||
Effect.tap(() => Effect.sync(() => (event.sdk = output.sdk))),
|
||||
)
|
||||
})
|
||||
}
|
||||
const run = callback as AISDKHooks["language"]
|
||||
return plugin.hook("aisdk.language", (event) => {
|
||||
const output = { ...event }
|
||||
const result = run(output)
|
||||
return Effect.suspend(() => (Effect.isEffect(result) ? result : Effect.void)).pipe(
|
||||
Effect.tap(() => Effect.sync(() => (event.language = output.language))),
|
||||
)
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function agentHost(agent: AgentV2.Interface): PluginHost["agent"] {
|
||||
return {
|
||||
...host().agent,
|
||||
transform: (callback) =>
|
||||
agent.transform((draft) =>
|
||||
callback({
|
||||
list: () => draft.list().map(agentInfo),
|
||||
get: (id) => {
|
||||
const value = draft.get(AgentV2.ID.make(id))
|
||||
return value && agentInfo(value)
|
||||
},
|
||||
default: (id) => draft.default(id === undefined ? undefined : AgentV2.ID.make(id)),
|
||||
update: (id, update) =>
|
||||
draft.update(AgentV2.ID.make(id), (value) => {
|
||||
const current = agentInfo(value)
|
||||
update(current)
|
||||
Object.assign(value, current, { id: AgentV2.ID.make(current.id) })
|
||||
}),
|
||||
remove: (id) => draft.remove(AgentV2.ID.make(id)),
|
||||
}),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
export function catalogHost(catalog: Catalog.Interface): PluginHost["catalog"] {
|
||||
return {
|
||||
...host().catalog,
|
||||
rebuild: catalog.rebuild,
|
||||
transform: (callback) =>
|
||||
catalog.transform((draft) =>
|
||||
callback({
|
||||
provider: {
|
||||
list: () =>
|
||||
draft.provider.list().map((value) => ({
|
||||
provider: providerInfo(value.provider),
|
||||
models: new Map(Array.from(value.models, ([id, model]) => [id, modelInfo(model)])),
|
||||
})),
|
||||
get: (id) => {
|
||||
const value = draft.provider.get(ProviderV2.ID.make(id))
|
||||
return (
|
||||
value && {
|
||||
provider: providerInfo(value.provider),
|
||||
models: new Map(Array.from(value.models, ([id, model]) => [id, modelInfo(model)])),
|
||||
}
|
||||
)
|
||||
},
|
||||
update: (id, update) =>
|
||||
draft.provider.update(ProviderV2.ID.make(id), (value) => {
|
||||
const current = providerInfo(value)
|
||||
update(current)
|
||||
Object.assign(value, current, { id: ProviderV2.ID.make(current.id) })
|
||||
}),
|
||||
remove: (id) => draft.provider.remove(ProviderV2.ID.make(id)),
|
||||
},
|
||||
model: {
|
||||
get: (providerID, modelID) => {
|
||||
const value = draft.model.get(ProviderV2.ID.make(providerID), ModelV2.ID.make(modelID))
|
||||
return value && modelInfo(value)
|
||||
},
|
||||
update: (providerID, modelID, update) =>
|
||||
draft.model.update(ProviderV2.ID.make(providerID), ModelV2.ID.make(modelID), (value) => {
|
||||
const current = modelInfo(value)
|
||||
update(current)
|
||||
Object.assign(value, current, {
|
||||
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) => ({
|
||||
...variant,
|
||||
id: ModelV2.VariantID.make(variant.id),
|
||||
})),
|
||||
})
|
||||
}),
|
||||
remove: (providerID, modelID) =>
|
||||
draft.model.remove(ProviderV2.ID.make(providerID), ModelV2.ID.make(modelID)),
|
||||
default: {
|
||||
get: () => {
|
||||
const value = draft.model.default.get()
|
||||
return value && { providerID: value.providerID, modelID: value.modelID }
|
||||
},
|
||||
set: (providerID, modelID) =>
|
||||
draft.model.default.set(ProviderV2.ID.make(providerID), ModelV2.ID.make(modelID)),
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
export function integrationHost(integration: Integration.Interface): PluginHost["integration"] {
|
||||
const info = (value: Integration.Info) => ({
|
||||
id: value.id,
|
||||
name: value.name,
|
||||
methods: value.methods.map(method),
|
||||
connections: value.connections.map((item) => ({ ...item })),
|
||||
})
|
||||
return {
|
||||
get: (id) => integration.get(Integration.ID.make(id)).pipe(Effect.map((value) => value && info(value))),
|
||||
list: () => integration.list().pipe(Effect.map((items) => items.map(info))),
|
||||
rebuild: integration.rebuild,
|
||||
transform: (callback) =>
|
||||
integration.transform((draft) =>
|
||||
callback({
|
||||
list: () => draft.list().map((value) => ({ id: value.id, name: value.name })),
|
||||
get: (id) => {
|
||||
const value = draft.get(Integration.ID.make(id))
|
||||
return value && { id: value.id, name: value.name }
|
||||
},
|
||||
update: (id, update) => draft.update(Integration.ID.make(id), update),
|
||||
remove: (id) => draft.remove(Integration.ID.make(id)),
|
||||
method: {
|
||||
list: (id) => draft.method.list(Integration.ID.make(id)).map(method),
|
||||
update: (input) =>
|
||||
input.method.type === "env"
|
||||
? draft.method.update({
|
||||
integrationID: Integration.ID.make(input.integrationID),
|
||||
method: { ...input.method, names: [...input.method.names] },
|
||||
})
|
||||
: draft.method.update({
|
||||
integrationID: Integration.ID.make(input.integrationID),
|
||||
method: input.method,
|
||||
}),
|
||||
remove: (id, item) => draft.method.remove(Integration.ID.make(id), internalMethod(item)),
|
||||
},
|
||||
}),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
function method(value: Integration.Method) {
|
||||
if (value.type === "env") return { type: value.type, names: [...value.names] }
|
||||
if (value.type === "key") return { type: value.type, label: value.label }
|
||||
return {
|
||||
type: value.type,
|
||||
id: value.id,
|
||||
label: value.label,
|
||||
prompts: value.prompts?.map((prompt) => {
|
||||
if (prompt.type === "text") return { ...prompt }
|
||||
return { ...prompt, options: prompt.options.map((option) => ({ ...option })) }
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
function internalMethod(value: IntegrationOAuthMethod | IntegrationKeyMethod | IntegrationEnvMethod): Integration.Method {
|
||||
if (value.type === "env") return value
|
||||
if (value.type === "key") return value
|
||||
return {
|
||||
...value,
|
||||
id: Integration.MethodID.make(value.id),
|
||||
}
|
||||
}
|
||||
|
||||
function agentInfo(value: AgentV2.Info) {
|
||||
return {
|
||||
...value,
|
||||
model: value.model && { ...value.model },
|
||||
request: { headers: { ...value.request.headers }, body: { ...value.request.body } },
|
||||
permissions: value.permissions.map((permission) => ({ ...permission })),
|
||||
}
|
||||
}
|
||||
|
||||
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 } },
|
||||
}
|
||||
}
|
||||
|
||||
function modelInfo(value: ModelV2.Info | ModelV2.MutableInfo) {
|
||||
return {
|
||||
...value,
|
||||
api: { ...value.api, settings: value.api.settings && { ...value.api.settings } },
|
||||
capabilities: {
|
||||
...value.capabilities,
|
||||
input: [...value.capabilities.input],
|
||||
output: [...value.capabilities.output],
|
||||
},
|
||||
request: {
|
||||
...value.request,
|
||||
headers: { ...value.request.headers },
|
||||
body: { ...value.request.body },
|
||||
generation: value.request.generation && {
|
||||
...value.request.generation,
|
||||
stop: value.request.generation.stop && [...value.request.generation.stop],
|
||||
},
|
||||
options: value.request.options && { ...value.request.options },
|
||||
},
|
||||
variants: value.variants.map((variant) => ({
|
||||
...variant,
|
||||
headers: { ...variant.headers },
|
||||
body: { ...variant.body },
|
||||
generation: variant.generation && {
|
||||
...variant.generation,
|
||||
stop: variant.generation.stop && [...variant.generation.stop],
|
||||
},
|
||||
options: variant.options && { ...variant.options },
|
||||
})),
|
||||
time: { ...value.time },
|
||||
cost: value.cost.map((cost) => ({ ...cost, tier: cost.tier && { ...cost.tier }, cache: { ...cost.cache } })),
|
||||
limit: { ...value.limit },
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import path from "path"
|
||||
import { describe, expect } from "bun:test"
|
||||
import { Effect, Layer } from "effect"
|
||||
import { Effect, 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"
|
||||
|
|
@ -15,6 +15,7 @@ import { Policy } from "@opencode-ai/core/policy"
|
|||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||
import { location } from "../fixture/location"
|
||||
import { testEffect } from "../lib/effect"
|
||||
import { catalogHost, host, integrationHost } from "./host"
|
||||
|
||||
const events = EventV2.defaultLayer
|
||||
const locationLayer = Layer.succeed(
|
||||
|
|
@ -56,8 +57,15 @@ describe("ModelsDevPlugin", () => {
|
|||
}),
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
yield* ModelsDevPlugin.effect
|
||||
const integrations = yield* Integration.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* ModelsDevPlugin.effect(
|
||||
host({
|
||||
catalog: catalogHost(catalog),
|
||||
event: { subscribe: () => Stream.never },
|
||||
integration: integrationHost(integrations),
|
||||
}),
|
||||
)
|
||||
expect(yield* integrations.list()).toEqual([
|
||||
new Integration.Info({
|
||||
id: Integration.ID.make("acme"),
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ import { Effect } from "effect"
|
|||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { AlibabaPlugin } from "@opencode-ai/core/plugin/provider/alibaba"
|
||||
import { it, model } from "./provider-helper"
|
||||
import { addPlugin, it, model } from "./provider-helper"
|
||||
|
||||
describe("AlibabaPlugin", () => {
|
||||
it.effect("creates an Alibaba SDK for @ai-sdk/alibaba", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(AlibabaPlugin)
|
||||
yield* addPlugin(plugin, AlibabaPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{ model: model("alibaba", "qwen"), package: "@ai-sdk/alibaba", options: { name: "alibaba" } },
|
||||
|
|
@ -23,7 +23,7 @@ describe("AlibabaPlugin", () => {
|
|||
it.effect("ignores non-Alibaba SDK packages", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(AlibabaPlugin)
|
||||
yield* addPlugin(plugin, AlibabaPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{ model: model("alibaba", "qwen"), package: "@ai-sdk/openai-compatible", options: { name: "alibaba" } },
|
||||
|
|
@ -36,7 +36,7 @@ describe("AlibabaPlugin", () => {
|
|||
it.effect("matches the old bundled Alibaba SDK provider naming", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(AlibabaPlugin)
|
||||
yield* addPlugin(plugin, AlibabaPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -56,7 +56,7 @@ describe("AlibabaPlugin", () => {
|
|||
it.effect("uses the old default languageModel(api.id) behavior", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(AlibabaPlugin)
|
||||
yield* addPlugin(plugin, AlibabaPlugin)
|
||||
const item = model("alibaba", "alias", { api: { id: ModelV2.ID.make("qwen-plus") } })
|
||||
const result = yield* plugin.trigger("aisdk.sdk", { model: item, package: "@ai-sdk/alibaba", options: {} }, {})
|
||||
const language = result.sdk?.languageModel(item.api.id)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { Catalog } from "@opencode-ai/core/catalog"
|
|||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { AmazonBedrockPlugin } from "@opencode-ai/core/plugin/provider/amazon-bedrock"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { fakeSelectorSdk, it, model, provider, withEnv } from "./provider-helper"
|
||||
import { addPlugin, fakeSelectorSdk, it, model, provider, required, withEnv } from "./provider-helper"
|
||||
|
||||
function bedrockBaseURL(sdk: unknown, modelID = "anthropic.claude-sonnet-4-5") {
|
||||
const language = (sdk as { languageModel: (id: string) => unknown }).languageModel(modelID)
|
||||
|
|
@ -30,9 +30,8 @@ describe("AmazonBedrockPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(AmazonBedrockPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, AmazonBedrockPlugin)
|
||||
yield* catalog.transform((catalog) => {
|
||||
const bedrock = provider("amazon-bedrock", {
|
||||
api: { type: "aisdk", package: "@ai-sdk/amazon-bedrock" },
|
||||
request: {
|
||||
|
|
@ -45,7 +44,7 @@ describe("AmazonBedrockPlugin", () => {
|
|||
item.request = bedrock.request
|
||||
})
|
||||
})
|
||||
const result = yield* catalog.provider.get(ProviderV2.ID.amazonBedrock)
|
||||
const result = required(yield* catalog.provider.get(ProviderV2.ID.amazonBedrock))
|
||||
expect(result.api).toEqual({
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/amazon-bedrock",
|
||||
|
|
@ -59,7 +58,7 @@ describe("AmazonBedrockPlugin", () => {
|
|||
withEnv({ AWS_BEARER_TOKEN_BEDROCK: undefined, AWS_PROFILE: undefined, AWS_ACCESS_KEY_ID: undefined }, () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(AmazonBedrockPlugin)
|
||||
yield* addPlugin(plugin, AmazonBedrockPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -84,7 +83,7 @@ describe("AmazonBedrockPlugin", () => {
|
|||
withEnv({ AWS_BEARER_TOKEN_BEDROCK: undefined, AWS_PROFILE: undefined, AWS_ACCESS_KEY_ID: undefined }, () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(AmazonBedrockPlugin)
|
||||
yield* addPlugin(plugin, AmazonBedrockPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -118,7 +117,7 @@ describe("AmazonBedrockPlugin", () => {
|
|||
() =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(AmazonBedrockPlugin)
|
||||
yield* addPlugin(plugin, AmazonBedrockPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -138,7 +137,7 @@ describe("AmazonBedrockPlugin", () => {
|
|||
withEnv({ AWS_BEARER_TOKEN_BEDROCK: "token", AWS_REGION: "us-east-1" }, () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(AmazonBedrockPlugin)
|
||||
yield* addPlugin(plugin, AmazonBedrockPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -157,7 +156,7 @@ describe("AmazonBedrockPlugin", () => {
|
|||
withEnv({ AWS_BEARER_TOKEN_BEDROCK: "token", AWS_REGION: "eu-west-1" }, () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(AmazonBedrockPlugin)
|
||||
yield* addPlugin(plugin, AmazonBedrockPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -176,7 +175,7 @@ describe("AmazonBedrockPlugin", () => {
|
|||
withEnv({ AWS_BEARER_TOKEN_BEDROCK: "token", AWS_REGION: undefined }, () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(AmazonBedrockPlugin)
|
||||
yield* addPlugin(plugin, AmazonBedrockPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -196,7 +195,7 @@ describe("AmazonBedrockPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const headers: Array<string | null> = []
|
||||
yield* plugin.add(AmazonBedrockPlugin)
|
||||
yield* addPlugin(plugin, AmazonBedrockPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -225,7 +224,7 @@ describe("AmazonBedrockPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const headers: Array<string | null> = []
|
||||
yield* plugin.add(AmazonBedrockPlugin)
|
||||
yield* addPlugin(plugin, AmazonBedrockPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -253,7 +252,7 @@ describe("AmazonBedrockPlugin", () => {
|
|||
withEnv({ AWS_BEARER_TOKEN_BEDROCK: undefined, AWS_PROFILE: undefined, AWS_ACCESS_KEY_ID: undefined }, () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(AmazonBedrockPlugin)
|
||||
yield* addPlugin(plugin, AmazonBedrockPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -282,7 +281,7 @@ describe("AmazonBedrockPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(AmazonBedrockPlugin)
|
||||
yield* addPlugin(plugin, AmazonBedrockPlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
@ -312,7 +311,7 @@ describe("AmazonBedrockPlugin", () => {
|
|||
it.effect("ignores other Bedrock provider subpaths", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(AmazonBedrockPlugin)
|
||||
yield* addPlugin(plugin, AmazonBedrockPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -341,7 +340,7 @@ describe("AmazonBedrockPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const headers: Array<string | null> = []
|
||||
yield* plugin.add(AmazonBedrockPlugin)
|
||||
yield* addPlugin(plugin, AmazonBedrockPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -372,7 +371,7 @@ describe("AmazonBedrockPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(AmazonBedrockPlugin)
|
||||
yield* addPlugin(plugin, AmazonBedrockPlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
@ -433,7 +432,7 @@ describe("AmazonBedrockPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(AmazonBedrockPlugin)
|
||||
yield* addPlugin(plugin, AmazonBedrockPlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
@ -518,7 +517,7 @@ describe("AmazonBedrockPlugin", () => {
|
|||
expected: "au.anthropic.claude-sonnet-4-5",
|
||||
},
|
||||
]
|
||||
yield* plugin.add(AmazonBedrockPlugin)
|
||||
yield* addPlugin(plugin, AmazonBedrockPlugin)
|
||||
for (const item of cases) {
|
||||
yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
|
|
@ -538,7 +537,7 @@ describe("AmazonBedrockPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(AmazonBedrockPlugin)
|
||||
yield* addPlugin(plugin, AmazonBedrockPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,16 +4,15 @@ import { Catalog } from "@opencode-ai/core/catalog"
|
|||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { AnthropicPlugin } from "@opencode-ai/core/plugin/provider/anthropic"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { it, model, provider } from "./provider-helper"
|
||||
import { addPlugin, it, model, provider, required } from "./provider-helper"
|
||||
|
||||
describe("AnthropicPlugin", () => {
|
||||
it.effect("applies legacy beta headers", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(AnthropicPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, AnthropicPlugin)
|
||||
yield* catalog.transform((catalog) => {
|
||||
const item = provider("anthropic", {
|
||||
api: { type: "aisdk", package: "@ai-sdk/anthropic" },
|
||||
request: { headers: { Existing: "1" }, body: {} },
|
||||
|
|
@ -23,10 +22,10 @@ describe("AnthropicPlugin", () => {
|
|||
draft.request = item.request
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.anthropic)).request.headers["anthropic-beta"]).toBe(
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.anthropic)).request.headers["anthropic-beta"]).toBe(
|
||||
"interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14",
|
||||
)
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.anthropic)).request.headers.Existing).toBe("1")
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.anthropic)).request.headers.Existing).toBe("1")
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -34,10 +33,9 @@ describe("AnthropicPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(AnthropicPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => catalog.provider.update(provider("openai").id, () => {}))
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.openai)).request.headers["anthropic-beta"]).toBeUndefined()
|
||||
yield* addPlugin(plugin, AnthropicPlugin)
|
||||
yield* catalog.transform((catalog) => catalog.provider.update(provider("openai").id, () => {}))
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.openai)).request.headers["anthropic-beta"]).toBeUndefined()
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -45,7 +43,7 @@ describe("AnthropicPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const providers: string[] = []
|
||||
yield* plugin.add(AnthropicPlugin)
|
||||
yield* addPlugin(plugin, AnthropicPlugin)
|
||||
yield* plugin.add({
|
||||
id: PluginV2.ID.make("anthropic-sdk-inspector"),
|
||||
effect: Effect.succeed({
|
||||
|
|
@ -72,7 +70,7 @@ describe("AnthropicPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const providers: string[] = []
|
||||
yield* plugin.add(AnthropicPlugin)
|
||||
yield* addPlugin(plugin, AnthropicPlugin)
|
||||
yield* plugin.add({
|
||||
id: PluginV2.ID.make("anthropic-sdk-inspector"),
|
||||
effect: Effect.succeed({
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { Catalog } from "@opencode-ai/core/catalog"
|
|||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { AzureCognitiveServicesPlugin } from "@opencode-ai/core/plugin/provider/azure"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { fakeSelectorSdk, it, model, provider, withEnv } from "./provider-helper"
|
||||
import { addPlugin, fakeSelectorSdk, it, model, provider, required, withEnv } from "./provider-helper"
|
||||
|
||||
describe("AzureCognitiveServicesPlugin", () => {
|
||||
it.effect("maps the resource env var to the Azure SDK baseURL", () =>
|
||||
|
|
@ -12,14 +12,13 @@ describe("AzureCognitiveServicesPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(AzureCognitiveServicesPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, AzureCognitiveServicesPlugin)
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.make("azure-cognitive-services"), (item) => {
|
||||
item.api = { type: "aisdk", package: "@ai-sdk/openai-compatible" }
|
||||
})
|
||||
})
|
||||
const result = yield* catalog.provider.get(ProviderV2.ID.make("azure-cognitive-services"))
|
||||
const result = required(yield* catalog.provider.get(ProviderV2.ID.make("azure-cognitive-services")))
|
||||
expect(result.api).toEqual({
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
|
|
@ -36,9 +35,8 @@ describe("AzureCognitiveServicesPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(AzureCognitiveServicesPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, AzureCognitiveServicesPlugin)
|
||||
yield* catalog.transform((catalog) => {
|
||||
const azure = provider("azure-cognitive-services", {
|
||||
api: { type: "aisdk", package: "@ai-sdk/openai-compatible" },
|
||||
})
|
||||
|
|
@ -50,8 +48,8 @@ describe("AzureCognitiveServicesPlugin", () => {
|
|||
item.api = openai.api
|
||||
})
|
||||
})
|
||||
const azure = yield* catalog.provider.get(ProviderV2.ID.make("azure-cognitive-services"))
|
||||
const openai = yield* catalog.provider.get(ProviderV2.ID.openai)
|
||||
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()
|
||||
|
|
@ -64,7 +62,7 @@ describe("AzureCognitiveServicesPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(AzureCognitiveServicesPlugin)
|
||||
yield* addPlugin(plugin, AzureCognitiveServicesPlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
@ -82,7 +80,7 @@ describe("AzureCognitiveServicesPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(AzureCognitiveServicesPlugin)
|
||||
yield* addPlugin(plugin, AzureCognitiveServicesPlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{ model: model("azure-cognitive-services", "deployment"), sdk: fakeSelectorSdk(calls), options: {} },
|
||||
|
|
@ -103,7 +101,7 @@ describe("AzureCognitiveServicesPlugin", () => {
|
|||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
const sdk = fakeSelectorSdk(calls)
|
||||
yield* plugin.add(AzureCognitiveServicesPlugin)
|
||||
yield* addPlugin(plugin, AzureCognitiveServicesPlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,35 +1,10 @@
|
|||
import { describe, expect } from "bun:test"
|
||||
import { Effect, Layer } from "effect"
|
||||
import { Credential } from "@opencode-ai/core/credential"
|
||||
import { Integration } from "@opencode-ai/core/integration"
|
||||
import { Database } from "@opencode-ai/core/database/database"
|
||||
import { Effect } from "effect"
|
||||
import { Catalog } from "@opencode-ai/core/catalog"
|
||||
import { EventV2 } from "@opencode-ai/core/event"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { AzurePlugin } from "@opencode-ai/core/plugin/provider/azure"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||
import { location } from "../fixture/location"
|
||||
import { testEffect } from "../lib/effect"
|
||||
import { fakeSelectorSdk, it, model, npmLayer, provider, withEnv } from "./provider-helper"
|
||||
|
||||
const database = Database.layerFromPath(":memory:").pipe(Layer.fresh)
|
||||
const preferences = Credential.layer.pipe(Layer.provide(database))
|
||||
const accounts = Layer.merge(
|
||||
Credential.layer.pipe(Layer.provide(database), Layer.provide(preferences), Layer.provide(EventV2.defaultLayer)),
|
||||
preferences,
|
||||
)
|
||||
const itWithAccount = testEffect(
|
||||
Catalog.locationLayer.pipe(
|
||||
Layer.provideMerge(accounts),
|
||||
Layer.provideMerge(EventV2.defaultLayer),
|
||||
Layer.provideMerge(
|
||||
Layer.succeed(Location.Service, Location.Service.of(location({ directory: AbsolutePath.make("test") }))),
|
||||
),
|
||||
Layer.provideMerge(npmLayer),
|
||||
),
|
||||
)
|
||||
import { addPlugin, fakeSelectorSdk, it, model, provider, required, withEnv } from "./provider-helper"
|
||||
|
||||
describe("AzurePlugin", () => {
|
||||
it.effect("resolves resourceName from env", () =>
|
||||
|
|
@ -37,14 +12,13 @@ describe("AzurePlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(AzurePlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.azure, (item) => {
|
||||
item.api = { type: "aisdk", package: "@ai-sdk/azure" }
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.azure)).request.body.resourceName).toBe("from-env")
|
||||
yield* addPlugin(plugin, AzurePlugin)
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.azure)).request.body.resourceName).toBe("from-env")
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -54,9 +28,7 @@ describe("AzurePlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(AzurePlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* catalog.transform((catalog) => {
|
||||
const azure = provider("azure", {
|
||||
api: { type: "aisdk", package: "@ai-sdk/azure" },
|
||||
request: { headers: {}, body: { resourceName: "from-config" } },
|
||||
|
|
@ -67,50 +39,19 @@ describe("AzurePlugin", () => {
|
|||
})
|
||||
catalog.provider.update(ProviderV2.ID.openai, () => {})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.azure)).request.body.resourceName).toBe("from-config")
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.openai)).request.body.resourceName).toBeUndefined()
|
||||
yield* addPlugin(plugin, AzurePlugin)
|
||||
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()
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
itWithAccount.effect("prefers account resourceName over env", () =>
|
||||
withEnv(
|
||||
{
|
||||
AZURE_RESOURCE_NAME: "from-env",
|
||||
},
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const credentials = yield* Credential.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* credentials.create({
|
||||
integrationID: Integration.ID.make("azure"),
|
||||
value: new Credential.Key({
|
||||
type: "key",
|
||||
key: "key",
|
||||
metadata: { resourceName: "from-account" },
|
||||
}),
|
||||
})
|
||||
yield* plugin.add(AzurePlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.azure, (item) => {
|
||||
item.api = { type: "aisdk", package: "@ai-sdk/azure" }
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.azure)).request.body.resourceName).toBe("from-account")
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
it.effect("falls back to env when configured resourceName is blank", () =>
|
||||
withEnv({ AZURE_RESOURCE_NAME: "from-env" }, () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(AzurePlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* catalog.transform((catalog) => {
|
||||
const azure = provider("azure", {
|
||||
api: { type: "aisdk", package: "@ai-sdk/azure" },
|
||||
request: { headers: {}, body: { resourceName: "" } },
|
||||
|
|
@ -120,7 +61,8 @@ describe("AzurePlugin", () => {
|
|||
item.request = azure.request
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.azure)).request.body.resourceName).toBe("from-env")
|
||||
yield* addPlugin(plugin, AzurePlugin)
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.azure)).request.body.resourceName).toBe("from-env")
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -130,9 +72,7 @@ describe("AzurePlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(AzurePlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* catalog.transform((catalog) => {
|
||||
const azure = provider("azure", {
|
||||
api: { type: "aisdk", package: "@ai-sdk/azure" },
|
||||
request: { headers: {}, body: { resourceName: " " } },
|
||||
|
|
@ -142,7 +82,8 @@ describe("AzurePlugin", () => {
|
|||
item.request = azure.request
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.azure)).request.body.resourceName).toBe("from-env")
|
||||
yield* addPlugin(plugin, AzurePlugin)
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.azure)).request.body.resourceName).toBe("from-env")
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -151,7 +92,7 @@ describe("AzurePlugin", () => {
|
|||
withEnv({ AZURE_RESOURCE_NAME: undefined }, () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(AzurePlugin)
|
||||
yield* addPlugin(plugin, AzurePlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -170,7 +111,7 @@ describe("AzurePlugin", () => {
|
|||
withEnv({ AZURE_RESOURCE_NAME: undefined }, () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(AzurePlugin)
|
||||
yield* addPlugin(plugin, AzurePlugin)
|
||||
const exit = yield* plugin
|
||||
.trigger(
|
||||
"aisdk.sdk",
|
||||
|
|
@ -187,7 +128,7 @@ describe("AzurePlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(AzurePlugin)
|
||||
yield* addPlugin(plugin, AzurePlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{ model: model("azure", "deployment"), sdk: fakeSelectorSdk(calls), options: { useCompletionUrls: true } },
|
||||
|
|
@ -201,7 +142,7 @@ describe("AzurePlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(AzurePlugin)
|
||||
yield* addPlugin(plugin, AzurePlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{ model: model("azure", "deployment"), sdk: fakeSelectorSdk(calls), options: { useCompletionUrls: true } },
|
||||
|
|
@ -215,7 +156,7 @@ describe("AzurePlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(AzurePlugin)
|
||||
yield* addPlugin(plugin, AzurePlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
@ -235,7 +176,7 @@ describe("AzurePlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(AzurePlugin)
|
||||
yield* addPlugin(plugin, AzurePlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{ model: model("azure", "deployment"), sdk: fakeSelectorSdk(calls), options: {} },
|
||||
|
|
@ -259,7 +200,7 @@ describe("AzurePlugin", () => {
|
|||
calls.push(`${method}:${id}`)
|
||||
return { modelId: id, provider: method, specificationVersion: "v3" }
|
||||
}
|
||||
yield* plugin.add(AzurePlugin)
|
||||
yield* addPlugin(plugin, AzurePlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { Catalog } from "@opencode-ai/core/catalog"
|
|||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { CerebrasPlugin } from "@opencode-ai/core/plugin/provider/cerebras"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { it, model } from "./provider-helper"
|
||||
import { addPlugin, it, model, required } from "./provider-helper"
|
||||
|
||||
const cerebrasOptions: Record<string, unknown>[] = []
|
||||
|
||||
|
|
@ -23,15 +23,14 @@ describe("CerebrasPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(CerebrasPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, CerebrasPlugin)
|
||||
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"
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("cerebras"))).request.headers).toEqual({
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("cerebras"))).request.headers).toEqual({
|
||||
Existing: "1",
|
||||
"X-Cerebras-3rd-Party-Integration": "opencode",
|
||||
})
|
||||
|
|
@ -42,10 +41,9 @@ describe("CerebrasPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(CerebrasPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => catalog.provider.update(ProviderV2.ID.make("groq"), () => {}))
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("groq"))).request.headers).toEqual({})
|
||||
yield* addPlugin(plugin, CerebrasPlugin)
|
||||
yield* catalog.transform((catalog) => catalog.provider.update(ProviderV2.ID.make("groq"), () => {}))
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("groq"))).request.headers).toEqual({})
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -53,7 +51,7 @@ describe("CerebrasPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
cerebrasOptions.length = 0
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(CerebrasPlugin)
|
||||
yield* addPlugin(plugin, CerebrasPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -72,7 +70,7 @@ describe("CerebrasPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
cerebrasOptions.length = 0
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(CerebrasPlugin)
|
||||
yield* addPlugin(plugin, CerebrasPlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -90,7 +88,7 @@ describe("CerebrasPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
cerebrasOptions.length = 0
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(CerebrasPlugin)
|
||||
yield* addPlugin(plugin, CerebrasPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { describe, expect, mock } from "bun:test"
|
|||
import { Effect } from "effect"
|
||||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { CloudflareAIGatewayPlugin } from "@opencode-ai/core/plugin/provider/cloudflare-ai-gateway"
|
||||
import { it, model, withEnv } from "./provider-helper"
|
||||
import { addPlugin, it, model, withEnv } from "./provider-helper"
|
||||
|
||||
const aiGatewayCalls: Record<string, unknown>[] = []
|
||||
const unifiedCalls: string[] = []
|
||||
|
|
@ -78,7 +78,7 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
() =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(CloudflareAIGatewayPlugin)
|
||||
yield* addPlugin(plugin, CloudflareAIGatewayPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -98,7 +98,7 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
resetCalls()
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(CloudflareAIGatewayPlugin)
|
||||
yield* addPlugin(plugin, CloudflareAIGatewayPlugin)
|
||||
|
||||
yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
|
|
@ -142,7 +142,7 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
resetCalls()
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(CloudflareAIGatewayPlugin)
|
||||
yield* addPlugin(plugin, CloudflareAIGatewayPlugin)
|
||||
|
||||
yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
|
|
@ -171,7 +171,7 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
resetCalls()
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(CloudflareAIGatewayPlugin)
|
||||
yield* addPlugin(plugin, CloudflareAIGatewayPlugin)
|
||||
|
||||
yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
|
|
@ -208,7 +208,7 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
resetCalls()
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(CloudflareAIGatewayPlugin)
|
||||
yield* addPlugin(plugin, CloudflareAIGatewayPlugin)
|
||||
|
||||
yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
|
|
@ -239,7 +239,7 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
resetCalls()
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(CloudflareAIGatewayPlugin)
|
||||
yield* addPlugin(plugin, CloudflareAIGatewayPlugin)
|
||||
|
||||
yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
|
|
@ -261,7 +261,7 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
resetCalls()
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(CloudflareAIGatewayPlugin)
|
||||
yield* addPlugin(plugin, CloudflareAIGatewayPlugin)
|
||||
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
|
|
@ -284,7 +284,7 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
resetCalls()
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(CloudflareAIGatewayPlugin)
|
||||
yield* addPlugin(plugin, CloudflareAIGatewayPlugin)
|
||||
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
|
|
@ -313,7 +313,7 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
resetCalls()
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(CloudflareAIGatewayPlugin)
|
||||
yield* addPlugin(plugin, CloudflareAIGatewayPlugin)
|
||||
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
|
|
@ -336,7 +336,7 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
resetCalls()
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(CloudflareAIGatewayPlugin)
|
||||
yield* addPlugin(plugin, CloudflareAIGatewayPlugin)
|
||||
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
|
|
@ -364,7 +364,7 @@ describe("CloudflareAIGatewayPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
resetCalls()
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(CloudflareAIGatewayPlugin)
|
||||
yield* addPlugin(plugin, CloudflareAIGatewayPlugin)
|
||||
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
|
|
|
|||
|
|
@ -1,36 +1,11 @@
|
|||
import { describe, expect } from "bun:test"
|
||||
import { Effect, Layer } from "effect"
|
||||
import { Credential } from "@opencode-ai/core/credential"
|
||||
import { Integration } from "@opencode-ai/core/integration"
|
||||
import { Database } from "@opencode-ai/core/database/database"
|
||||
import { Effect } from "effect"
|
||||
import { Catalog } from "@opencode-ai/core/catalog"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
import { EventV2 } from "@opencode-ai/core/event"
|
||||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { CloudflareWorkersAIPlugin } from "@opencode-ai/core/plugin/provider/cloudflare-workers-ai"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||
import { location } from "../fixture/location"
|
||||
import { testEffect } from "../lib/effect"
|
||||
import { fakeSelectorSdk, it, model, npmLayer, withEnv } from "./provider-helper"
|
||||
|
||||
const database = Database.layerFromPath(":memory:").pipe(Layer.fresh)
|
||||
const preferences = Credential.layer.pipe(Layer.provide(database))
|
||||
const accounts = Layer.merge(
|
||||
Credential.layer.pipe(Layer.provide(database), Layer.provide(preferences), Layer.provide(EventV2.defaultLayer)),
|
||||
preferences,
|
||||
)
|
||||
const itWithAccount = testEffect(
|
||||
Catalog.locationLayer.pipe(
|
||||
Layer.provideMerge(accounts),
|
||||
Layer.provideMerge(EventV2.defaultLayer),
|
||||
Layer.provideMerge(
|
||||
Layer.succeed(Location.Service, Location.Service.of(location({ directory: AbsolutePath.make("test") }))),
|
||||
),
|
||||
Layer.provideMerge(npmLayer),
|
||||
),
|
||||
)
|
||||
import { addPlugin, fakeSelectorSdk, it, model, required, withEnv } from "./provider-helper"
|
||||
|
||||
function cloudflareLanguage(sdk: unknown, modelID = "@cf/model") {
|
||||
return (sdk as { languageModel: (id: string) => { config: CloudflareConfig; provider: string } }).languageModel(
|
||||
|
|
@ -57,14 +32,13 @@ describe("CloudflareWorkersAIPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(CloudflareWorkersAIPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) =>
|
||||
yield* catalog.transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.make("cloudflare-workers-ai"), (provider) => {
|
||||
provider.api = { type: "aisdk", package: "test-provider" }
|
||||
}),
|
||||
)
|
||||
const provider = yield* catalog.provider.get(ProviderV2.ID.make("cloudflare-workers-ai"))
|
||||
yield* addPlugin(plugin, CloudflareWorkersAIPlugin)
|
||||
const provider = required(yield* catalog.provider.get(ProviderV2.ID.make("cloudflare-workers-ai")))
|
||||
const sdk = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -89,14 +63,13 @@ describe("CloudflareWorkersAIPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(CloudflareWorkersAIPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) =>
|
||||
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" }
|
||||
}),
|
||||
)
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("cloudflare-workers-ai"))).api).toEqual({
|
||||
yield* addPlugin(plugin, CloudflareWorkersAIPlugin)
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("cloudflare-workers-ai"))).api).toEqual({
|
||||
type: "aisdk",
|
||||
package: "test-provider",
|
||||
url: "https://proxy.example/v1",
|
||||
|
|
@ -109,7 +82,7 @@ describe("CloudflareWorkersAIPlugin", () => {
|
|||
withEnv({ CLOUDFLARE_ACCOUNT_ID: undefined, CLOUDFLARE_API_KEY: "key" }, () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(CloudflareWorkersAIPlugin)
|
||||
yield* addPlugin(plugin, CloudflareWorkersAIPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -126,56 +99,19 @@ describe("CloudflareWorkersAIPlugin", () => {
|
|||
),
|
||||
)
|
||||
|
||||
itWithAccount.effect("falls back to account metadata when account env is absent", () =>
|
||||
withEnv(
|
||||
{
|
||||
CLOUDFLARE_ACCOUNT_ID: undefined,
|
||||
CLOUDFLARE_API_KEY: undefined,
|
||||
},
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const credentials = yield* Credential.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* credentials.create({
|
||||
integrationID: Integration.ID.make("cloudflare-workers-ai"),
|
||||
value: new Credential.Key({
|
||||
type: "key",
|
||||
key: "account-key",
|
||||
metadata: { accountId: "account-acct" },
|
||||
}),
|
||||
})
|
||||
yield* plugin.add(CloudflareWorkersAIPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.make("cloudflare-workers-ai"), (provider) => {
|
||||
provider.api = { type: "aisdk", package: "test-provider" }
|
||||
}),
|
||||
)
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("cloudflare-workers-ai"))).request.body).toMatchObject(
|
||||
{
|
||||
apiKey: "account-key",
|
||||
accountId: "account-acct",
|
||||
},
|
||||
)
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
it.effect("uses env account ID over configured account ID", () =>
|
||||
withEnv({ CLOUDFLARE_ACCOUNT_ID: "env-acct" }, () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(CloudflareWorkersAIPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) =>
|
||||
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"
|
||||
}),
|
||||
)
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("cloudflare-workers-ai"))).api).toEqual({
|
||||
yield* addPlugin(plugin, CloudflareWorkersAIPlugin)
|
||||
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",
|
||||
|
|
@ -188,7 +124,7 @@ describe("CloudflareWorkersAIPlugin", () => {
|
|||
withEnv({ CLOUDFLARE_ACCOUNT_ID: "acct", CLOUDFLARE_API_KEY: "env-key" }, () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(CloudflareWorkersAIPlugin)
|
||||
yield* addPlugin(plugin, CloudflareWorkersAIPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -217,7 +153,7 @@ describe("CloudflareWorkersAIPlugin", () => {
|
|||
withEnv({ CLOUDFLARE_ACCOUNT_ID: "acct", CLOUDFLARE_API_KEY: "key" }, () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(CloudflareWorkersAIPlugin)
|
||||
yield* addPlugin(plugin, CloudflareWorkersAIPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -247,7 +183,7 @@ describe("CloudflareWorkersAIPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(CloudflareWorkersAIPlugin)
|
||||
yield* addPlugin(plugin, CloudflareWorkersAIPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
@ -266,7 +202,7 @@ describe("CloudflareWorkersAIPlugin", () => {
|
|||
withEnv({ CLOUDFLARE_ACCOUNT_ID: "acct", CLOUDFLARE_API_KEY: "key" }, () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(CloudflareWorkersAIPlugin)
|
||||
yield* addPlugin(plugin, CloudflareWorkersAIPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { Effect } from "effect"
|
|||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { CoherePlugin } from "@opencode-ai/core/plugin/provider/cohere"
|
||||
import { fakeSelectorSdk, it, model } from "./provider-helper"
|
||||
import { addPlugin, fakeSelectorSdk, it, model } from "./provider-helper"
|
||||
|
||||
const cohereOptions: Record<string, any>[] = []
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ describe("CoherePlugin", () => {
|
|||
it.effect("creates a Cohere SDK only for @ai-sdk/cohere", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(CoherePlugin)
|
||||
yield* addPlugin(plugin, CoherePlugin)
|
||||
|
||||
const ignored = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
|
|
@ -45,7 +45,7 @@ describe("CoherePlugin", () => {
|
|||
it.effect("uses the model provider ID as the bundled SDK name", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(CoherePlugin)
|
||||
yield* addPlugin(plugin, CoherePlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -70,7 +70,7 @@ describe("CoherePlugin", () => {
|
|||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
const sdk = fakeSelectorSdk(calls)
|
||||
yield* plugin.add(CoherePlugin)
|
||||
yield* addPlugin(plugin, CoherePlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{ model: model("cohere", "alias", { api: { id: ModelV2.ID.make("command-r-plus") } }), sdk, options: {} },
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { EventV2 } from "@opencode-ai/core/event"
|
|||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { DeepInfraPlugin } from "@opencode-ai/core/plugin/provider/deepinfra"
|
||||
import { testEffect } from "../lib/effect"
|
||||
import { it, model } from "./provider-helper"
|
||||
import { addPlugin, it, model } from "./provider-helper"
|
||||
|
||||
const itAISDK = testEffect(
|
||||
Layer.provideMerge(AISDK.layer, PluginV2.locationLayer.pipe(Layer.provide(EventV2.defaultLayer))),
|
||||
|
|
@ -36,7 +36,7 @@ describe("DeepInfraPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
resetDeepInfraMock()
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(DeepInfraPlugin)
|
||||
yield* addPlugin(plugin, DeepInfraPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{ model: model("deepinfra", "model"), package: "@ai-sdk/deepinfra", options: { name: "deepinfra" } },
|
||||
|
|
@ -50,7 +50,7 @@ describe("DeepInfraPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
resetDeepInfraMock()
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(DeepInfraPlugin)
|
||||
yield* addPlugin(plugin, DeepInfraPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -69,7 +69,7 @@ describe("DeepInfraPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
resetDeepInfraMock()
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(DeepInfraPlugin)
|
||||
yield* addPlugin(plugin, DeepInfraPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -88,7 +88,7 @@ describe("DeepInfraPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
resetDeepInfraMock()
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(DeepInfraPlugin)
|
||||
yield* addPlugin(plugin, DeepInfraPlugin)
|
||||
const packages = [
|
||||
"unmatched-package",
|
||||
"@ai-sdk/deepinfra-compatible",
|
||||
|
|
@ -119,7 +119,7 @@ describe("DeepInfraPlugin", () => {
|
|||
resetDeepInfraMock()
|
||||
const plugin = yield* PluginV2.Service
|
||||
const aisdk = yield* AISDK.Service
|
||||
yield* plugin.add(DeepInfraPlugin)
|
||||
yield* addPlugin(plugin, DeepInfraPlugin)
|
||||
const language = yield* aisdk.language(
|
||||
model("deepinfra", "meta-llama/Llama-3.3-70B-Instruct", {
|
||||
api: { type: "aisdk", package: "@ai-sdk/deepinfra" },
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { ModelV2 } from "@opencode-ai/core/model"
|
|||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { DynamicProviderPlugin } from "@opencode-ai/core/plugin/provider/dynamic"
|
||||
import { testEffect } from "../lib/effect"
|
||||
import { host } from "./host"
|
||||
import { fixtureProvider, it, model, npmLayer } from "./provider-helper"
|
||||
|
||||
const fixtureProviderPath = fileURLToPath(fixtureProvider)
|
||||
|
|
@ -18,19 +19,24 @@ const itWithAISDK = testEffect(
|
|||
AISDK.layer.pipe(Layer.provideMerge(PluginV2.locationLayer.pipe(Layer.provide(EventV2.defaultLayer)))),
|
||||
)
|
||||
|
||||
function npmEntrypointLayer(entrypoint: Option.Option<string>) {
|
||||
function npmEntrypointLayer(entrypoint?: string) {
|
||||
return Layer.succeed(
|
||||
Npm.Service,
|
||||
Npm.Service.of({
|
||||
add: () => Effect.succeed({ directory: "", entrypoint }),
|
||||
install: () => Effect.void,
|
||||
which: () => Effect.succeed(Option.none<string>()),
|
||||
which: () => Effect.succeed(undefined),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
function dynamicPlugin(layer = npmLayer) {
|
||||
return { id: DynamicProviderPlugin.id, effect: DynamicProviderPlugin.effect.pipe(Effect.provide(layer)) }
|
||||
return {
|
||||
id: DynamicProviderPlugin.id,
|
||||
effect: Effect.gen(function* () {
|
||||
yield* DynamicProviderPlugin.effect(host({ npm: yield* Npm.Service }))
|
||||
}).pipe(Effect.provide(layer)),
|
||||
}
|
||||
}
|
||||
|
||||
function tempEntrypoint(source: string) {
|
||||
|
|
@ -102,7 +108,7 @@ describe("DynamicProviderPlugin", () => {
|
|||
it.effect("loads npm packages through their resolved import entrypoint", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(dynamicPlugin(npmEntrypointLayer(Option.some(fixtureProviderPath))))
|
||||
yield* plugin.add(dynamicPlugin(npmEntrypointLayer(fixtureProviderPath)))
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -120,7 +126,7 @@ describe("DynamicProviderPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const aisdk = yield* AISDK.Service
|
||||
yield* plugin.add(dynamicPlugin(npmEntrypointLayer(Option.none<string>())))
|
||||
yield* plugin.add(dynamicPlugin(npmEntrypointLayer()))
|
||||
const exit = yield* aisdk
|
||||
.language(model("missing-entrypoint", "alias", { api: { type: "aisdk", package: "fixture-provider" } }))
|
||||
.pipe(Effect.exit)
|
||||
|
|
@ -149,7 +155,7 @@ describe("DynamicProviderPlugin", () => {
|
|||
const plugin = yield* PluginV2.Service
|
||||
const aisdk = yield* AISDK.Service
|
||||
const tmp = yield* tempEntrypoint("export const notAProviderFactory = true\n")
|
||||
yield* plugin.add(dynamicPlugin(npmEntrypointLayer(Option.some(tmp.entrypoint))))
|
||||
yield* plugin.add(dynamicPlugin(npmEntrypointLayer(tmp.entrypoint)))
|
||||
const exit = yield* aisdk
|
||||
.language(model("missing-factory", "alias", { api: { type: "aisdk", package: "fixture-provider" } }))
|
||||
.pipe(Effect.exit)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { describe, expect, mock } from "bun:test"
|
|||
import { Effect } from "effect"
|
||||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { GatewayPlugin } from "@opencode-ai/core/plugin/provider/gateway"
|
||||
import { it, model } from "./provider-helper"
|
||||
import { addPlugin, it, model } from "./provider-helper"
|
||||
|
||||
const gatewayCalls: Record<string, unknown>[] = []
|
||||
const vercelGatewayModels = ["anthropic/claude-sonnet-4", "openai/gpt-5", "google/gemini-2.5-pro"]
|
||||
|
|
@ -27,7 +27,7 @@ describe("GatewayPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
gatewayCalls.length = 0
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(GatewayPlugin)
|
||||
yield* addPlugin(plugin, GatewayPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{ model: model("gateway", "model"), package: "@ai-sdk/gateway", options: { name: "gateway" } },
|
||||
|
|
@ -42,7 +42,7 @@ describe("GatewayPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
gatewayCalls.length = 0
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(GatewayPlugin)
|
||||
yield* addPlugin(plugin, GatewayPlugin)
|
||||
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
|
|
@ -63,7 +63,7 @@ describe("GatewayPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
gatewayCalls.length = 0
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(GatewayPlugin)
|
||||
yield* addPlugin(plugin, GatewayPlugin)
|
||||
|
||||
for (const modelID of vercelGatewayModels) {
|
||||
const ignored = yield* plugin.trigger(
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@ import { ModelV2 } from "@opencode-ai/core/model"
|
|||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { GithubCopilotPlugin } from "@opencode-ai/core/plugin/provider/github-copilot"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { fakeSelectorSdk, it, model } from "./provider-helper"
|
||||
import { addPlugin, fakeSelectorSdk, it, model, required } from "./provider-helper"
|
||||
|
||||
describe("GithubCopilotPlugin", () => {
|
||||
it.effect("creates the bundled Copilot SDK for the GitHub Copilot package", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(GithubCopilotPlugin)
|
||||
yield* addPlugin(plugin, GithubCopilotPlugin)
|
||||
const ignored = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -39,7 +39,7 @@ describe("GithubCopilotPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(GithubCopilotPlugin)
|
||||
yield* addPlugin(plugin, GithubCopilotPlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
@ -57,7 +57,7 @@ describe("GithubCopilotPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(GithubCopilotPlugin)
|
||||
yield* addPlugin(plugin, GithubCopilotPlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
@ -75,7 +75,7 @@ describe("GithubCopilotPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(GithubCopilotPlugin)
|
||||
yield* addPlugin(plugin, GithubCopilotPlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{ model: model("github-copilot", "gpt-5"), sdk: fakeSelectorSdk(calls), options: {} },
|
||||
|
|
@ -115,7 +115,7 @@ describe("GithubCopilotPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(GithubCopilotPlugin)
|
||||
yield* addPlugin(plugin, GithubCopilotPlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
@ -151,14 +151,13 @@ describe("GithubCopilotPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(GithubCopilotPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, GithubCopilotPlugin)
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.make("github-copilot"), () => {})
|
||||
catalog.model.update(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-5-chat-latest"), () => {})
|
||||
})
|
||||
expect(
|
||||
(yield* catalog.model.get(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-5-chat-latest"))).enabled,
|
||||
required(yield* catalog.model.get(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-5-chat-latest"))).enabled,
|
||||
).toBe(false)
|
||||
}),
|
||||
)
|
||||
|
|
@ -167,14 +166,13 @@ describe("GithubCopilotPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(GithubCopilotPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, GithubCopilotPlugin)
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.make("custom-copilot"), () => {})
|
||||
catalog.model.update(ProviderV2.ID.make("custom-copilot"), ModelV2.ID.make("gpt-5-chat-latest"), () => {})
|
||||
})
|
||||
expect(
|
||||
(yield* catalog.model.get(ProviderV2.ID.make("custom-copilot"), ModelV2.ID.make("gpt-5-chat-latest"))).enabled,
|
||||
required(yield* catalog.model.get(ProviderV2.ID.make("custom-copilot"), ModelV2.ID.make("gpt-5-chat-latest"))).enabled,
|
||||
).toBe(true)
|
||||
}),
|
||||
)
|
||||
|
|
@ -183,7 +181,7 @@ describe("GithubCopilotPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(GithubCopilotPlugin)
|
||||
yield* addPlugin(plugin, GithubCopilotPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{ model: model("openai", "gpt-5"), sdk: fakeSelectorSdk(calls), options: {} },
|
||||
|
|
|
|||
|
|
@ -1,26 +1,12 @@
|
|||
import { describe, expect, mock } from "bun:test"
|
||||
import { Effect, Layer } from "effect"
|
||||
import { Credential } from "@opencode-ai/core/credential"
|
||||
import { Integration } from "@opencode-ai/core/integration"
|
||||
import { Database } from "@opencode-ai/core/database/database"
|
||||
import { Effect } from "effect"
|
||||
import { Catalog } from "@opencode-ai/core/catalog"
|
||||
import { EventV2 } from "@opencode-ai/core/event"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { GitLabPlugin } from "@opencode-ai/core/plugin/provider/gitlab"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||
import { location } from "../fixture/location"
|
||||
import { testEffect } from "../lib/effect"
|
||||
import { it, model, npmLayer, withEnv } from "./provider-helper"
|
||||
import { addPlugin, it, model, required, withEnv } from "./provider-helper"
|
||||
|
||||
const gitlabSDKOptions: Record<string, unknown>[] = []
|
||||
const database = Database.layerFromPath(":memory:").pipe(Layer.fresh)
|
||||
const preferences = Credential.layer.pipe(Layer.provide(database))
|
||||
const accounts = Layer.merge(
|
||||
Credential.layer.pipe(Layer.provide(database), Layer.provide(preferences), Layer.provide(EventV2.defaultLayer)),
|
||||
preferences,
|
||||
)
|
||||
|
||||
void mock.module("gitlab-ai-provider", () => ({
|
||||
VERSION: "test-version",
|
||||
|
|
@ -35,17 +21,6 @@ void mock.module("gitlab-ai-provider", () => ({
|
|||
isWorkflowModel: (id: string) => id === "duo-workflow" || id === "duo-workflow-exact",
|
||||
}))
|
||||
|
||||
const itWithAccount = testEffect(
|
||||
Catalog.locationLayer.pipe(
|
||||
Layer.provideMerge(accounts),
|
||||
Layer.provideMerge(EventV2.defaultLayer),
|
||||
Layer.provideMerge(
|
||||
Layer.succeed(Location.Service, Location.Service.of(location({ directory: AbsolutePath.make("/") }))),
|
||||
),
|
||||
Layer.provideMerge(npmLayer),
|
||||
),
|
||||
)
|
||||
|
||||
describe("GitLabPlugin", () => {
|
||||
it.effect("creates SDKs with legacy default instance URL, token env, headers, and feature flags", () =>
|
||||
withEnv(
|
||||
|
|
@ -57,7 +32,7 @@ describe("GitLabPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
gitlabSDKOptions.length = 0
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(GitLabPlugin)
|
||||
yield* addPlugin(plugin, GitLabPlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{ model: model("gitlab", "claude"), package: "gitlab-ai-provider", options: { name: "gitlab" } },
|
||||
|
|
@ -90,7 +65,7 @@ describe("GitLabPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
gitlabSDKOptions.length = 0
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(GitLabPlugin)
|
||||
yield* addPlugin(plugin, GitLabPlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{ model: model("gitlab", "claude"), package: "gitlab-ai-provider", options: { name: "gitlab" } },
|
||||
|
|
@ -111,7 +86,7 @@ describe("GitLabPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
gitlabSDKOptions.length = 0
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(GitLabPlugin)
|
||||
yield* addPlugin(plugin, GitLabPlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -152,7 +127,7 @@ describe("GitLabPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
gitlabSDKOptions.length = 0
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(GitLabPlugin)
|
||||
yield* addPlugin(plugin, GitLabPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{ model: model("gitlab", "claude"), package: "@ai-sdk/openai", options: { name: "gitlab" } },
|
||||
|
|
@ -163,83 +138,11 @@ describe("GitLabPlugin", () => {
|
|||
}),
|
||||
)
|
||||
|
||||
itWithAccount.effect("uses active account API token over GITLAB_TOKEN", () =>
|
||||
withEnv(
|
||||
{
|
||||
GITLAB_TOKEN: "env-token",
|
||||
},
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
gitlabSDKOptions.length = 0
|
||||
const plugin = yield* PluginV2.Service
|
||||
const credentials = yield* Credential.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* credentials.create({
|
||||
integrationID: Integration.ID.make("gitlab"),
|
||||
value: new Credential.Key({ type: "key", key: "account-token" }),
|
||||
})
|
||||
yield* plugin.add(GitLabPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => catalog.provider.update(ProviderV2.ID.make("gitlab"), () => {}))
|
||||
const provider = yield* catalog.provider.get(ProviderV2.ID.make("gitlab"))
|
||||
yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
model: model("gitlab", "claude"),
|
||||
package: "gitlab-ai-provider",
|
||||
options: provider.request.body,
|
||||
},
|
||||
{},
|
||||
)
|
||||
expect(gitlabSDKOptions[0].apiKey).toBe("account-token")
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
itWithAccount.effect("uses active account OAuth access token when no API token exists", () =>
|
||||
withEnv(
|
||||
{
|
||||
GITLAB_TOKEN: undefined,
|
||||
},
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
gitlabSDKOptions.length = 0
|
||||
const plugin = yield* PluginV2.Service
|
||||
const credentials = yield* Credential.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* credentials.create({
|
||||
integrationID: Integration.ID.make("gitlab"),
|
||||
value: new Credential.OAuth({
|
||||
type: "oauth",
|
||||
methodID: Integration.MethodID.make("oauth"),
|
||||
refresh: "refresh-token",
|
||||
access: "account-oauth-token",
|
||||
expires: 9999999999999,
|
||||
}),
|
||||
})
|
||||
yield* plugin.add(GitLabPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => catalog.provider.update(ProviderV2.ID.make("gitlab"), () => {}))
|
||||
const provider = yield* catalog.provider.get(ProviderV2.ID.make("gitlab"))
|
||||
yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
model: model("gitlab", "claude"),
|
||||
package: "gitlab-ai-provider",
|
||||
options: provider.request.body,
|
||||
},
|
||||
{},
|
||||
)
|
||||
expect(gitlabSDKOptions[0].apiKey).toBe("account-oauth-token")
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
it.effect("uses workflowChat for duo workflow models and preserves selectedModelRef", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: [string, unknown][] = []
|
||||
yield* plugin.add(GitLabPlugin)
|
||||
yield* addPlugin(plugin, GitLabPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
@ -275,7 +178,7 @@ describe("GitLabPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: [string, unknown][] = []
|
||||
yield* plugin.add(GitLabPlugin)
|
||||
yield* addPlugin(plugin, GitLabPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
@ -302,7 +205,7 @@ describe("GitLabPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: [string, unknown][] = []
|
||||
yield* plugin.add(GitLabPlugin)
|
||||
yield* addPlugin(plugin, GitLabPlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
@ -331,7 +234,7 @@ describe("GitLabPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: [string, unknown][] = []
|
||||
yield* plugin.add(GitLabPlugin)
|
||||
yield* addPlugin(plugin, GitLabPlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { Catalog } from "@opencode-ai/core/catalog"
|
|||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { GoogleVertexAnthropicPlugin, GoogleVertexPlugin } from "@opencode-ai/core/plugin/provider/google-vertex"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { fakeSelectorSdk, it, model, withEnv } from "./provider-helper"
|
||||
import { addPlugin, fakeSelectorSdk, it, model, required, withEnv } from "./provider-helper"
|
||||
|
||||
describe("GoogleVertexAnthropicPlugin", () => {
|
||||
it.effect("resolves legacy project and location env on provider update", () =>
|
||||
|
|
@ -21,14 +21,13 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(GoogleVertexAnthropicPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) =>
|
||||
yield* addPlugin(plugin, GoogleVertexAnthropicPlugin)
|
||||
yield* catalog.transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.make("google-vertex-anthropic"), (provider) => {
|
||||
provider.api = { type: "aisdk", package: "@ai-sdk/google-vertex/anthropic" }
|
||||
}),
|
||||
)
|
||||
const provider = yield* catalog.provider.get(ProviderV2.ID.make("google-vertex-anthropic"))
|
||||
const provider = required(yield* catalog.provider.get(ProviderV2.ID.make("google-vertex-anthropic")))
|
||||
expect(provider.request.body.project).toBe("cloud-project")
|
||||
expect(provider.request.body.location).toBe("cloud-location")
|
||||
}),
|
||||
|
|
@ -40,16 +39,15 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(GoogleVertexAnthropicPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) =>
|
||||
yield* addPlugin(plugin, GoogleVertexAnthropicPlugin)
|
||||
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"
|
||||
}),
|
||||
)
|
||||
const provider = yield* catalog.provider.get(ProviderV2.ID.make("google-vertex-anthropic"))
|
||||
const provider = required(yield* catalog.provider.get(ProviderV2.ID.make("google-vertex-anthropic")))
|
||||
expect(provider.request.body.project).toBe("configured-project")
|
||||
expect(provider.request.body.location).toBe("configured-location")
|
||||
}),
|
||||
|
|
@ -69,7 +67,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||
() =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(GoogleVertexAnthropicPlugin)
|
||||
yield* addPlugin(plugin, GoogleVertexAnthropicPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -92,7 +90,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||
() =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(GoogleVertexAnthropicPlugin)
|
||||
yield* addPlugin(plugin, GoogleVertexAnthropicPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -112,7 +110,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||
it.effect("creates SDKs for google-vertex Anthropic models with multi-region endpoints", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(GoogleVertexAnthropicPlugin)
|
||||
yield* addPlugin(plugin, GoogleVertexAnthropicPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -131,7 +129,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||
it.effect("keeps configured baseURL for google-vertex Anthropic models", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(GoogleVertexAnthropicPlugin)
|
||||
yield* addPlugin(plugin, GoogleVertexAnthropicPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -148,8 +146,8 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||
it.effect("selects google-vertex Anthropic language models through V2 plugins", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(GoogleVertexPlugin)
|
||||
yield* plugin.add(GoogleVertexAnthropicPlugin)
|
||||
yield* addPlugin(plugin, GoogleVertexPlugin)
|
||||
yield* addPlugin(plugin, GoogleVertexAnthropicPlugin)
|
||||
const sdkResult = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -180,7 +178,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(GoogleVertexAnthropicPlugin)
|
||||
yield* addPlugin(plugin, GoogleVertexAnthropicPlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
@ -198,7 +196,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(GoogleVertexAnthropicPlugin)
|
||||
yield* addPlugin(plugin, GoogleVertexAnthropicPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { Catalog } from "@opencode-ai/core/catalog"
|
|||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { GoogleVertexPlugin } from "@opencode-ai/core/plugin/provider/google-vertex"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { fakeSelectorSdk, it, model, withEnv } from "./provider-helper"
|
||||
import { addPlugin, fakeSelectorSdk, it, model, required, withEnv } from "./provider-helper"
|
||||
|
||||
const vertexOptions: Record<string, any>[] = []
|
||||
const googleAuthOptions: Record<string, any>[] = []
|
||||
|
|
@ -39,9 +39,8 @@ describe("GoogleVertexPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(GoogleVertexPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) =>
|
||||
yield* addPlugin(plugin, GoogleVertexPlugin)
|
||||
yield* catalog.transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.opencode, (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
|
|
@ -51,7 +50,7 @@ describe("GoogleVertexPlugin", () => {
|
|||
}),
|
||||
)
|
||||
|
||||
const provider = yield* catalog.provider.get(ProviderV2.ID.opencode)
|
||||
const provider = required(yield* catalog.provider.get(ProviderV2.ID.opencode))
|
||||
expect(provider.request.body).toEqual({})
|
||||
}),
|
||||
)
|
||||
|
|
@ -70,9 +69,8 @@ describe("GoogleVertexPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(GoogleVertexPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) =>
|
||||
yield* addPlugin(plugin, GoogleVertexPlugin)
|
||||
yield* catalog.transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.make("google-vertex"), (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
|
|
@ -81,7 +79,7 @@ describe("GoogleVertexPlugin", () => {
|
|||
}
|
||||
}),
|
||||
)
|
||||
const provider = yield* catalog.provider.get(ProviderV2.ID.make("google-vertex"))
|
||||
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({
|
||||
|
|
@ -109,9 +107,8 @@ describe("GoogleVertexPlugin", () => {
|
|||
vertexOptions.length = 0
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(GoogleVertexPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) =>
|
||||
yield* addPlugin(plugin, GoogleVertexPlugin)
|
||||
yield* catalog.transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.make("google-vertex"), (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
|
|
@ -120,7 +117,7 @@ describe("GoogleVertexPlugin", () => {
|
|||
}
|
||||
}),
|
||||
)
|
||||
const provider = yield* catalog.provider.get(ProviderV2.ID.make("google-vertex"))
|
||||
const provider = required(yield* catalog.provider.get(ProviderV2.ID.make("google-vertex")))
|
||||
yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -159,9 +156,8 @@ describe("GoogleVertexPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(GoogleVertexPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) =>
|
||||
yield* addPlugin(plugin, GoogleVertexPlugin)
|
||||
yield* catalog.transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.make("google-vertex"), (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
|
|
@ -172,7 +168,7 @@ describe("GoogleVertexPlugin", () => {
|
|||
provider.request.body.location = "global"
|
||||
}),
|
||||
)
|
||||
const provider = yield* catalog.provider.get(ProviderV2.ID.make("google-vertex"))
|
||||
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({
|
||||
|
|
@ -188,9 +184,8 @@ describe("GoogleVertexPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(GoogleVertexPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) =>
|
||||
yield* addPlugin(plugin, GoogleVertexPlugin)
|
||||
yield* catalog.transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.make("google-vertex"), (provider) => {
|
||||
provider.api = {
|
||||
type: "aisdk",
|
||||
|
|
@ -201,7 +196,7 @@ describe("GoogleVertexPlugin", () => {
|
|||
provider.request.body.location = "eu"
|
||||
}),
|
||||
)
|
||||
const provider = yield* catalog.provider.get(ProviderV2.ID.make("google-vertex"))
|
||||
const provider = required(yield* catalog.provider.get(ProviderV2.ID.make("google-vertex")))
|
||||
expect(provider.api).toEqual({
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
|
|
@ -224,15 +219,14 @@ describe("GoogleVertexPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(GoogleVertexPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) =>
|
||||
yield* addPlugin(plugin, GoogleVertexPlugin)
|
||||
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"
|
||||
}),
|
||||
)
|
||||
const provider = yield* catalog.provider.get(ProviderV2.ID.make("google-vertex"))
|
||||
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")
|
||||
}),
|
||||
|
|
@ -249,7 +243,7 @@ describe("GoogleVertexPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
vertexOptions.length = 0
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(GoogleVertexPlugin)
|
||||
yield* addPlugin(plugin, GoogleVertexPlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -274,7 +268,7 @@ describe("GoogleVertexPlugin", () => {
|
|||
googleAuthOptions.length = 0
|
||||
const fetchCalls: { input: Parameters<typeof fetch>[0]; init?: RequestInit }[] = []
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(GoogleVertexPlugin)
|
||||
yield* addPlugin(plugin, GoogleVertexPlugin)
|
||||
yield* plugin.add({
|
||||
id: PluginV2.ID.make("capture-openai-compatible"),
|
||||
effect: Effect.succeed({
|
||||
|
|
@ -328,7 +322,7 @@ describe("GoogleVertexPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(GoogleVertexPlugin)
|
||||
yield* addPlugin(plugin, GoogleVertexPlugin)
|
||||
yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { ModelV2 } from "@opencode-ai/core/model"
|
|||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { GooglePlugin } from "@opencode-ai/core/plugin/provider/google"
|
||||
import { testEffect } from "../lib/effect"
|
||||
import { it, model } from "./provider-helper"
|
||||
import { addPlugin, it, model } from "./provider-helper"
|
||||
|
||||
const itWithAISDK = testEffect(
|
||||
AISDK.layer.pipe(Layer.provideMerge(PluginV2.locationLayer.pipe(Layer.provide(EventV2.defaultLayer)))),
|
||||
|
|
@ -16,7 +16,7 @@ describe("GooglePlugin", () => {
|
|||
it.effect("creates a Google Generative AI SDK for @ai-sdk/google using the provider ID as SDK name", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(GooglePlugin)
|
||||
yield* addPlugin(plugin, GooglePlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -34,7 +34,7 @@ describe("GooglePlugin", () => {
|
|||
it.effect("ignores non-Google SDK packages", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(GooglePlugin)
|
||||
yield* addPlugin(plugin, GooglePlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{ model: model("google", "gemini"), package: "@ai-sdk/google-vertex", options: { name: "google" } },
|
||||
|
|
@ -48,7 +48,7 @@ describe("GooglePlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const aisdk = yield* AISDK.Service
|
||||
yield* plugin.add(GooglePlugin)
|
||||
yield* addPlugin(plugin, GooglePlugin)
|
||||
const language = yield* aisdk.language(
|
||||
model("custom-google", "alias", {
|
||||
api: {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { EventV2 } from "@opencode-ai/core/event"
|
|||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { GroqPlugin } from "@opencode-ai/core/plugin/provider/groq"
|
||||
import { it, model } from "./provider-helper"
|
||||
import { addPlugin, it, model } from "./provider-helper"
|
||||
import { testEffect } from "../lib/effect"
|
||||
|
||||
const aisdkIt = testEffect(
|
||||
|
|
@ -17,7 +17,7 @@ describe("GroqPlugin", () => {
|
|||
it.effect("creates a Groq SDK for @ai-sdk/groq", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(GroqPlugin)
|
||||
yield* addPlugin(plugin, GroqPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{ model: model("groq", "llama"), package: "@ai-sdk/groq", options: { name: "groq" } },
|
||||
|
|
@ -30,7 +30,7 @@ describe("GroqPlugin", () => {
|
|||
it.effect("ignores non-Groq SDK packages", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(GroqPlugin)
|
||||
yield* addPlugin(plugin, GroqPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{ model: model("groq", "llama"), package: "@ai-sdk/openai-compatible", options: { name: "groq" } },
|
||||
|
|
@ -43,7 +43,7 @@ describe("GroqPlugin", () => {
|
|||
it.effect("only matches the bundled @ai-sdk/groq package exactly", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(GroqPlugin)
|
||||
yield* addPlugin(plugin, GroqPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{ model: model("groq", "llama"), package: "@ai-sdk/groq/compat", options: { name: "groq" } },
|
||||
|
|
@ -56,7 +56,7 @@ describe("GroqPlugin", () => {
|
|||
it.effect("matches the old bundled Groq SDK provider naming", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(GroqPlugin)
|
||||
yield* addPlugin(plugin, GroqPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -79,7 +79,7 @@ describe("GroqPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const aisdk = yield* AISDK.Service
|
||||
yield* plugin.add(GroqPlugin)
|
||||
yield* addPlugin(plugin, GroqPlugin)
|
||||
const result = yield* aisdk.language(
|
||||
model("groq", "alias", {
|
||||
api: {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { Npm } from "@opencode-ai/core/npm"
|
||||
import type { Plugin } from "@opencode-ai/plugin/v2/effect"
|
||||
import type { LanguageModelV3 } from "@ai-sdk/provider"
|
||||
import { expect } from "bun:test"
|
||||
import { Effect, Layer, Option } from "effect"
|
||||
|
|
@ -13,8 +14,15 @@ import { ProviderV2 } from "@opencode-ai/core/provider"
|
|||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||
import { location } from "../fixture/location"
|
||||
import { testEffect } from "../lib/effect"
|
||||
import { aisdkHost, catalogHost, host, integrationHost } from "./host"
|
||||
|
||||
export const fixtureProvider = new URL("./fixtures/provider-factory.ts", import.meta.url).href
|
||||
|
||||
export function required<T>(value: T | undefined): T {
|
||||
if (value === undefined) throw new Error("Expected value")
|
||||
return value
|
||||
}
|
||||
|
||||
const locationLayer = Layer.succeed(
|
||||
Location.Service,
|
||||
Location.Service.of(location({ directory: AbsolutePath.make("test") })),
|
||||
|
|
@ -23,16 +31,17 @@ const locationLayer = Layer.succeed(
|
|||
export const npmLayer = Layer.succeed(
|
||||
Npm.Service,
|
||||
Npm.Service.of({
|
||||
add: () => Effect.succeed({ directory: "", entrypoint: Option.none<string>() }),
|
||||
add: () => Effect.succeed({ directory: "", entrypoint: undefined }),
|
||||
install: () => Effect.void,
|
||||
which: () => Effect.succeed(Option.none<string>()),
|
||||
which: () => Effect.succeed(undefined),
|
||||
}),
|
||||
)
|
||||
|
||||
export const catalogLayer = Layer.succeed(
|
||||
Catalog.Service,
|
||||
Catalog.Service.of({
|
||||
transform: () => Effect.die("unexpected catalog.transform"),
|
||||
transform: (_transform) => Effect.die("unexpected catalog.transform"),
|
||||
rebuild: () => Effect.die("unexpected catalog.rebuild"),
|
||||
provider: {
|
||||
get: () => Effect.die("unexpected provider.get"),
|
||||
all: () => Effect.succeed([]),
|
||||
|
|
@ -42,8 +51,8 @@ export const catalogLayer = Layer.succeed(
|
|||
get: () => Effect.die("unexpected model.get"),
|
||||
all: () => Effect.succeed([]),
|
||||
available: () => Effect.succeed([]),
|
||||
default: () => Effect.succeed(Option.none<ModelV2.Info>()),
|
||||
small: () => Effect.succeed(Option.none<ModelV2.Info>()),
|
||||
default: () => Effect.succeed(undefined),
|
||||
small: () => Effect.succeed(undefined),
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
|
@ -70,9 +79,30 @@ export const it = testEffect(
|
|||
Layer.provideMerge(EventV2.defaultLayer),
|
||||
Layer.provideMerge(locationLayer),
|
||||
Layer.provideMerge(npmLayer),
|
||||
Layer.provideMerge(PluginV2.locationLayer.pipe(Layer.provide(EventV2.defaultLayer))),
|
||||
),
|
||||
)
|
||||
|
||||
export function addPlugin(plugin: PluginV2.Interface, definition: Plugin<any>) {
|
||||
return Effect.gen(function* () {
|
||||
const catalog = yield* Effect.serviceOption(Catalog.Service)
|
||||
const integration = yield* Effect.serviceOption(Integration.Service)
|
||||
const npm = yield* Effect.serviceOption(Npm.Service)
|
||||
const effect =
|
||||
typeof definition.effect === "function"
|
||||
? definition.effect(
|
||||
host({
|
||||
aisdk: aisdkHost(plugin),
|
||||
...(Option.isSome(catalog) ? { catalog: catalogHost(catalog.value) } : {}),
|
||||
...(Option.isSome(integration) ? { integration: integrationHost(integration.value) } : {}),
|
||||
...(Option.isSome(npm) ? { npm: npm.value } : {}),
|
||||
}),
|
||||
)
|
||||
: definition.effect
|
||||
yield* plugin.add({ id: definition.id, effect })
|
||||
})
|
||||
}
|
||||
|
||||
type ProviderInput = Partial<Omit<ProviderV2.Info, "api" | "request">> & {
|
||||
api?: ProviderV2.Api
|
||||
request?: ProviderV2.Request
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { PluginV2 } from "@opencode-ai/core/plugin"
|
|||
import { ProviderPlugins } from "@opencode-ai/core/plugin/provider"
|
||||
import { KiloPlugin } from "@opencode-ai/core/plugin/provider/kilo"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { expectPluginRegistered, it, provider } from "./provider-helper"
|
||||
import { addPlugin, expectPluginRegistered, it, provider, required } from "./provider-helper"
|
||||
|
||||
describe("KiloPlugin", () => {
|
||||
it.effect("is registered so legacy referer headers can be applied", () =>
|
||||
|
|
@ -21,9 +21,8 @@ describe("KiloPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(KiloPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, KiloPlugin)
|
||||
yield* catalog.transform((catalog) => {
|
||||
const kilo = provider("kilo", {
|
||||
api: { type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://api.kilo.ai/api/gateway" },
|
||||
request: { headers: { Existing: "value" }, body: {} },
|
||||
|
|
@ -34,12 +33,12 @@ describe("KiloPlugin", () => {
|
|||
})
|
||||
catalog.provider.update(provider("openrouter").id, () => {})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("kilo"))).request.headers).toEqual({
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("kilo"))).request.headers).toEqual({
|
||||
Existing: "value",
|
||||
"HTTP-Referer": "https://opencode.ai/",
|
||||
"X-Title": "opencode",
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.openrouter)).request.headers).toEqual({})
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.openrouter)).request.headers).toEqual({})
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -47,9 +46,8 @@ describe("KiloPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(KiloPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, KiloPlugin)
|
||||
yield* catalog.transform((catalog) => {
|
||||
const item = provider("kilo", {
|
||||
api: { type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://api.kilo.ai/api/gateway" },
|
||||
})
|
||||
|
|
@ -58,7 +56,7 @@ describe("KiloPlugin", () => {
|
|||
})
|
||||
})
|
||||
|
||||
const result = yield* catalog.provider.get(ProviderV2.ID.make("kilo"))
|
||||
const result = required(yield* catalog.provider.get(ProviderV2.ID.make("kilo")))
|
||||
expect(result.request.headers).toEqual({
|
||||
"HTTP-Referer": "https://opencode.ai/",
|
||||
"X-Title": "opencode",
|
||||
|
|
@ -73,9 +71,8 @@ describe("KiloPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(KiloPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, KiloPlugin)
|
||||
yield* catalog.transform((catalog) => {
|
||||
const kilo = provider("kilo", {
|
||||
api: { type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://api.kilo.ai/api/gateway" },
|
||||
})
|
||||
|
|
@ -90,11 +87,11 @@ describe("KiloPlugin", () => {
|
|||
})
|
||||
})
|
||||
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("kilo"))).request.headers).toEqual({
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("kilo"))).request.headers).toEqual({
|
||||
"HTTP-Referer": "https://opencode.ai/",
|
||||
"X-Title": "opencode",
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("custom-kilo"))).request.headers).toEqual({})
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("custom-kilo"))).request.headers).toEqual({})
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -6,14 +6,18 @@ import { PluginV2 } from "@opencode-ai/core/plugin"
|
|||
import { ProviderPlugins } from "@opencode-ai/core/plugin/provider"
|
||||
import { LLMGatewayPlugin } from "@opencode-ai/core/plugin/provider/llmgateway"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { expectPluginRegistered, it, provider } from "./provider-helper"
|
||||
import { expectPluginRegistered, it, provider, required } from "./provider-helper"
|
||||
import { catalogHost, host, integrationHost } from "./host"
|
||||
|
||||
describe("LLMGatewayPlugin", () => {
|
||||
const add = Effect.fnUntraced(function* (plugin: PluginV2.Interface) {
|
||||
const integrations = yield* Integration.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add({
|
||||
...LLMGatewayPlugin,
|
||||
effect: LLMGatewayPlugin.effect.pipe(Effect.provideService(Integration.Service, integrations)),
|
||||
effect: LLMGatewayPlugin.effect(
|
||||
host({ catalog: catalogHost(catalog), integration: integrationHost(integrations) }),
|
||||
),
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -30,14 +34,12 @@ describe("LLMGatewayPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* add(plugin)
|
||||
const integrations = yield* Integration.Service
|
||||
yield* integrations.update((editor) => {
|
||||
yield* integrations.transform((editor) => {
|
||||
editor.update(Integration.ID.make("llmgateway"), () => {})
|
||||
editor.update(Integration.ID.make("openrouter"), () => {})
|
||||
})
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* catalog.transform((catalog) => {
|
||||
const llmgateway = provider("llmgateway", {
|
||||
api: { type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://api.llmgateway.io/v1" },
|
||||
request: { headers: { Existing: "value" }, body: {} },
|
||||
|
|
@ -48,13 +50,14 @@ describe("LLMGatewayPlugin", () => {
|
|||
})
|
||||
catalog.provider.update(ProviderV2.ID.openrouter, () => {})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("llmgateway"))).request.headers).toEqual({
|
||||
yield* add(plugin)
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("llmgateway"))).request.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(required(yield* catalog.provider.get(ProviderV2.ID.openrouter)).request.headers).toEqual({})
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -63,8 +66,7 @@ describe("LLMGatewayPlugin", () => {
|
|||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* add(plugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* catalog.transform((catalog) => {
|
||||
const item = provider("llmgateway", {
|
||||
api: { type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://api.llmgateway.io/v1" },
|
||||
})
|
||||
|
|
@ -73,8 +75,8 @@ describe("LLMGatewayPlugin", () => {
|
|||
})
|
||||
})
|
||||
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("llmgateway"))).disabled).toBeUndefined()
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("llmgateway"))).request.headers).toEqual({})
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("llmgateway"))).disabled).toBeUndefined()
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("llmgateway"))).request.headers).toEqual({})
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ import { Effect } from "effect"
|
|||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { MistralPlugin } from "@opencode-ai/core/plugin/provider/mistral"
|
||||
import { fakeSelectorSdk, it, model } from "./provider-helper"
|
||||
import { addPlugin, fakeSelectorSdk, it, model } from "./provider-helper"
|
||||
|
||||
describe("MistralPlugin", () => {
|
||||
it.effect("creates a Mistral SDK for @ai-sdk/mistral", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(MistralPlugin)
|
||||
yield* addPlugin(plugin, MistralPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{ model: model("mistral", "mistral-large"), package: "@ai-sdk/mistral", options: { name: "mistral" } },
|
||||
|
|
@ -22,7 +22,7 @@ describe("MistralPlugin", () => {
|
|||
it.effect("ignores non-Mistral SDK packages", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(MistralPlugin)
|
||||
yield* addPlugin(plugin, MistralPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -40,7 +40,7 @@ describe("MistralPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const providers: string[] = []
|
||||
yield* plugin.add(MistralPlugin)
|
||||
yield* addPlugin(plugin, MistralPlugin)
|
||||
yield* plugin.add({
|
||||
id: PluginV2.ID.make("mistral-sdk-inspector"),
|
||||
effect: Effect.succeed({
|
||||
|
|
@ -64,7 +64,7 @@ describe("MistralPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const providers: string[] = []
|
||||
yield* plugin.add(MistralPlugin)
|
||||
yield* addPlugin(plugin, MistralPlugin)
|
||||
yield* plugin.add({
|
||||
id: PluginV2.ID.make("mistral-sdk-inspector"),
|
||||
effect: Effect.succeed({
|
||||
|
|
@ -92,7 +92,7 @@ describe("MistralPlugin", () => {
|
|||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
const sdk = fakeSelectorSdk(calls)
|
||||
yield* plugin.add(MistralPlugin)
|
||||
yield* addPlugin(plugin, MistralPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{ model: model("mistral", "alias", { api: { id: ModelV2.ID.make("mistral-large") } }), sdk, options: {} },
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { PluginV2 } from "@opencode-ai/core/plugin"
|
|||
import { ProviderPlugins } from "@opencode-ai/core/plugin/provider"
|
||||
import { NvidiaPlugin } from "@opencode-ai/core/plugin/provider/nvidia"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { expectPluginRegistered, it, provider } from "./provider-helper"
|
||||
import { addPlugin, expectPluginRegistered, it, provider, required } from "./provider-helper"
|
||||
|
||||
describe("NvidiaPlugin", () => {
|
||||
it.effect("is registered so legacy referer headers can be applied", () =>
|
||||
|
|
@ -21,9 +21,8 @@ describe("NvidiaPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(NvidiaPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, NvidiaPlugin)
|
||||
yield* catalog.transform((catalog) => {
|
||||
const nvidia = provider("nvidia", {
|
||||
api: { type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://integrate.api.nvidia.com/v1" },
|
||||
request: { headers: { Existing: "value" }, body: {} },
|
||||
|
|
@ -34,13 +33,13 @@ describe("NvidiaPlugin", () => {
|
|||
})
|
||||
catalog.provider.update(provider("openrouter").id, () => {})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("nvidia"))).request.headers).toEqual({
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("nvidia"))).request.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(required(yield* catalog.provider.get(ProviderV2.ID.openrouter)).request.headers).toEqual({})
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -48,9 +47,8 @@ describe("NvidiaPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(NvidiaPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, NvidiaPlugin)
|
||||
yield* catalog.transform((catalog) => {
|
||||
const item = provider("nvidia", {
|
||||
api: { type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://integrate.api.nvidia.com/v1" },
|
||||
request: { headers: {}, body: {} },
|
||||
|
|
@ -61,7 +59,7 @@ describe("NvidiaPlugin", () => {
|
|||
})
|
||||
})
|
||||
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("nvidia"))).request.headers).toEqual({
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("nvidia"))).request.headers).toEqual({
|
||||
"HTTP-Referer": "https://opencode.ai/",
|
||||
"X-Title": "opencode",
|
||||
"X-BILLING-INVOKE-ORIGIN": "OpenCode",
|
||||
|
|
@ -73,9 +71,8 @@ describe("NvidiaPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(NvidiaPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, NvidiaPlugin)
|
||||
yield* catalog.transform((catalog) => {
|
||||
const item = provider("nvidia", {
|
||||
api: { type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://integrate.api.nvidia.com/v1" },
|
||||
request: {
|
||||
|
|
@ -89,7 +86,7 @@ describe("NvidiaPlugin", () => {
|
|||
})
|
||||
})
|
||||
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("nvidia"))).request.headers).toEqual({
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("nvidia"))).request.headers).toEqual({
|
||||
"HTTP-Referer": "https://opencode.ai/",
|
||||
"X-Title": "opencode",
|
||||
"X-BILLING-INVOKE-ORIGIN": "CustomOrigin",
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ import { describe, expect } from "bun:test"
|
|||
import { Effect } from "effect"
|
||||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { OpenAICompatiblePlugin } from "@opencode-ai/core/plugin/provider/openai-compatible"
|
||||
import { it, model } from "./provider-helper"
|
||||
import { addPlugin, it, model } from "./provider-helper"
|
||||
|
||||
describe("OpenAICompatiblePlugin", () => {
|
||||
it.effect("preserves explicit includeUsage false and defaults it to true", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(OpenAICompatiblePlugin)
|
||||
yield* addPlugin(plugin, OpenAICompatiblePlugin)
|
||||
const defaulted = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{ model: model("custom", "model"), package: "@ai-sdk/openai-compatible", options: { name: "custom" } },
|
||||
|
|
@ -31,7 +31,7 @@ describe("OpenAICompatiblePlugin", () => {
|
|||
it.effect("defaults includeUsage for OpenAI-compatible package matches", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(OpenAICompatiblePlugin)
|
||||
yield* addPlugin(plugin, OpenAICompatiblePlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -49,7 +49,7 @@ describe("OpenAICompatiblePlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const observed: string[] = []
|
||||
yield* plugin.add(OpenAICompatiblePlugin)
|
||||
yield* addPlugin(plugin, OpenAICompatiblePlugin)
|
||||
yield* plugin.add({
|
||||
id: PluginV2.ID.make("inspector"),
|
||||
effect: Effect.succeed({
|
||||
|
|
@ -85,7 +85,7 @@ describe("OpenAICompatiblePlugin", () => {
|
|||
}),
|
||||
}),
|
||||
})
|
||||
yield* plugin.add(OpenAICompatiblePlugin)
|
||||
yield* addPlugin(plugin, OpenAICompatiblePlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,12 +6,15 @@ import { ModelV2 } from "@opencode-ai/core/model"
|
|||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { OpenAIPlugin } from "@opencode-ai/core/plugin/provider/openai"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { fakeSelectorSdk, it, model, provider } from "./provider-helper"
|
||||
import { fakeSelectorSdk, it, model, provider, required } from "./provider-helper"
|
||||
import { host, integrationHost } from "./host"
|
||||
|
||||
function add(plugin: PluginV2.Interface, integrations: Integration.Interface) {
|
||||
return plugin.add({
|
||||
...OpenAIPlugin,
|
||||
effect: OpenAIPlugin.effect.pipe(Effect.provideService(Integration.Service, integrations)),
|
||||
id: OpenAIPlugin.id,
|
||||
effect: OpenAIPlugin.effect(host({ integration: integrationHost(integrations) })).pipe(
|
||||
Effect.provideService(Integration.Service, integrations),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -106,8 +109,7 @@ describe("OpenAIPlugin", () => {
|
|||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* add(plugin, yield* Integration.Service)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* catalog.transform((catalog) => {
|
||||
const item = provider("openai", { api: { type: "aisdk", package: "@ai-sdk/openai" } })
|
||||
catalog.provider.update(item.id, (draft) => {
|
||||
draft.api = item.api
|
||||
|
|
@ -115,8 +117,8 @@ describe("OpenAIPlugin", () => {
|
|||
catalog.model.update(item.id, ModelV2.ID.make("gpt-5"), () => {})
|
||||
catalog.model.update(item.id, ModelV2.ID.make("gpt-5-chat-latest"), () => {})
|
||||
})
|
||||
expect((yield* catalog.model.get(ProviderV2.ID.openai, ModelV2.ID.make("gpt-5"))).enabled).toBe(true)
|
||||
expect((yield* catalog.model.get(ProviderV2.ID.openai, ModelV2.ID.make("gpt-5-chat-latest"))).enabled).toBe(false)
|
||||
expect(required(yield* catalog.model.get(ProviderV2.ID.openai, ModelV2.ID.make("gpt-5"))).enabled).toBe(true)
|
||||
expect(required(yield* catalog.model.get(ProviderV2.ID.openai, ModelV2.ID.make("gpt-5-chat-latest"))).enabled).toBe(false)
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -125,14 +127,13 @@ describe("OpenAIPlugin", () => {
|
|||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* add(plugin, yield* Integration.Service)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* catalog.transform((catalog) => {
|
||||
const item = provider("custom-openai")
|
||||
catalog.provider.update(item.id, () => {})
|
||||
catalog.model.update(item.id, ModelV2.ID.make("gpt-5-chat-latest"), () => {})
|
||||
})
|
||||
expect(
|
||||
(yield* catalog.model.get(ProviderV2.ID.make("custom-openai"), ModelV2.ID.make("gpt-5-chat-latest"))).enabled,
|
||||
required(yield* catalog.model.get(ProviderV2.ID.make("custom-openai"), ModelV2.ID.make("gpt-5-chat-latest"))).enabled,
|
||||
).toBe(true)
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { describe, expect } from "bun:test"
|
||||
import { DateTime, Effect, Layer, Option } from "effect"
|
||||
import { Effect, Layer, Option } from "effect"
|
||||
import { Catalog } from "@opencode-ai/core/catalog"
|
||||
import { Credential } from "@opencode-ai/core/credential"
|
||||
import { EventV2 } from "@opencode-ai/core/event"
|
||||
|
|
@ -11,7 +11,8 @@ import { OpencodePlugin } from "@opencode-ai/core/plugin/provider/opencode"
|
|||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||
import { location } from "../fixture/location"
|
||||
import { it, model, provider, withEnv } from "./provider-helper"
|
||||
import { it, model, provider, required, withEnv } from "./provider-helper"
|
||||
import { catalogHost, host, integrationHost } from "./host"
|
||||
|
||||
const cost = (input: number, output = 0) => [{ input, output, cache: { read: 0, write: 0 } }]
|
||||
const locationLayer = Layer.succeed(
|
||||
|
|
@ -19,9 +20,11 @@ const locationLayer = Layer.succeed(
|
|||
Location.Service.of(location({ directory: AbsolutePath.make("test") })),
|
||||
)
|
||||
|
||||
const pluginWithIntegrations = (integrations: Integration.Interface) => ({
|
||||
const pluginWithIntegrations = (catalog: Catalog.Interface, integrations: Integration.Interface) => ({
|
||||
...OpencodePlugin,
|
||||
effect: OpencodePlugin.effect.pipe(Effect.provideService(Integration.Service, integrations)),
|
||||
effect: OpencodePlugin.effect(
|
||||
host({ catalog: catalogHost(catalog), integration: integrationHost(integrations) }),
|
||||
),
|
||||
})
|
||||
|
||||
describe("OpencodePlugin", () => {
|
||||
|
|
@ -30,9 +33,8 @@ describe("OpencodePlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(pluginWithIntegrations(yield* Integration.Service))
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* plugin.add(pluginWithIntegrations(catalog, yield* Integration.Service))
|
||||
yield* catalog.transform((catalog) => {
|
||||
const item = provider("opencode")
|
||||
catalog.provider.update(item.id, () => {})
|
||||
const paid = model("opencode", "paid", { cost: cost(1) })
|
||||
|
|
@ -40,8 +42,8 @@ describe("OpencodePlugin", () => {
|
|||
draft.cost = [...paid.cost]
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).request.body.apiKey).toBe("public")
|
||||
expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(false)
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).request.body.apiKey).toBe("public")
|
||||
expect(required(yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(false)
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -51,9 +53,8 @@ describe("OpencodePlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(pluginWithIntegrations(yield* Integration.Service))
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* plugin.add(pluginWithIntegrations(catalog, yield* Integration.Service))
|
||||
yield* catalog.transform((catalog) => {
|
||||
const item = provider("opencode")
|
||||
catalog.provider.update(item.id, () => {})
|
||||
const free = model("opencode", "free", { cost: cost(0) })
|
||||
|
|
@ -61,8 +62,8 @@ describe("OpencodePlugin", () => {
|
|||
draft.cost = [...free.cost]
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).request.body.apiKey).toBe("public")
|
||||
expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("free"))).enabled).toBe(true)
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).request.body.apiKey).toBe("public")
|
||||
expect(required(yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("free"))).enabled).toBe(true)
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -72,9 +73,8 @@ describe("OpencodePlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(pluginWithIntegrations(yield* Integration.Service))
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* plugin.add(pluginWithIntegrations(catalog, yield* Integration.Service))
|
||||
yield* catalog.transform((catalog) => {
|
||||
const item = provider("opencode")
|
||||
catalog.provider.update(item.id, () => {})
|
||||
const outputOnly = model("opencode", "output-only", { cost: cost(0, 1) })
|
||||
|
|
@ -82,8 +82,8 @@ describe("OpencodePlugin", () => {
|
|||
draft.cost = [...outputOnly.cost]
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).request.body.apiKey).toBe("public")
|
||||
expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("output-only"))).enabled).toBe(true)
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).request.body.apiKey).toBe("public")
|
||||
expect(required(yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("output-only"))).enabled).toBe(true)
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -93,9 +93,8 @@ describe("OpencodePlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(pluginWithIntegrations(yield* Integration.Service))
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* plugin.add(pluginWithIntegrations(catalog, yield* Integration.Service))
|
||||
yield* catalog.transform((catalog) => {
|
||||
const item = provider("opencode")
|
||||
catalog.provider.update(item.id, () => {})
|
||||
const paid = model("opencode", "paid", { cost: cost(1) })
|
||||
|
|
@ -103,8 +102,8 @@ describe("OpencodePlugin", () => {
|
|||
draft.cost = [...paid.cost]
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).request.body.apiKey).toBeUndefined()
|
||||
expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).request.body.apiKey).toBeUndefined()
|
||||
expect(required(yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -115,15 +114,14 @@ describe("OpencodePlugin", () => {
|
|||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
const integrations = yield* Integration.Service
|
||||
yield* plugin.add(pluginWithIntegrations(integrations))
|
||||
yield* integrations.update((editor) => {
|
||||
yield* plugin.add(pluginWithIntegrations(catalog, integrations))
|
||||
yield* integrations.transform((editor) => {
|
||||
editor.method.update({
|
||||
integrationID: Integration.ID.make("opencode"),
|
||||
method: { type: "env", names: ["CUSTOM_OPENCODE_API_KEY"] },
|
||||
})
|
||||
})
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* catalog.transform((catalog) => {
|
||||
const item = provider("opencode")
|
||||
catalog.provider.update(item.id, () => {})
|
||||
const paid = model("opencode", "paid", { cost: cost(1) })
|
||||
|
|
@ -131,8 +129,8 @@ describe("OpencodePlugin", () => {
|
|||
draft.cost = [...paid.cost]
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).request.body.apiKey).toBeUndefined()
|
||||
expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).request.body.apiKey).toBeUndefined()
|
||||
expect(required(yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -142,9 +140,8 @@ describe("OpencodePlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(pluginWithIntegrations(yield* Integration.Service))
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* plugin.add(pluginWithIntegrations(catalog, yield* Integration.Service))
|
||||
yield* catalog.transform((catalog) => {
|
||||
const item = provider("opencode", {
|
||||
request: {
|
||||
headers: {},
|
||||
|
|
@ -159,8 +156,8 @@ describe("OpencodePlugin", () => {
|
|||
draft.cost = [...paid.cost]
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).request.body.apiKey).toBe("configured")
|
||||
expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.opencode)).request.body.apiKey).toBe("configured")
|
||||
expect(required(yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -170,9 +167,8 @@ describe("OpencodePlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(pluginWithIntegrations(yield* Integration.Service))
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* plugin.add(pluginWithIntegrations(catalog, yield* Integration.Service))
|
||||
yield* catalog.transform((catalog) => {
|
||||
const item = provider("openai")
|
||||
catalog.provider.update(item.id, () => {})
|
||||
const paid = model("openai", "paid", { cost: cost(1) })
|
||||
|
|
@ -180,8 +176,8 @@ describe("OpencodePlugin", () => {
|
|||
draft.cost = [...paid.cost]
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.openai)).request.body.apiKey).toBeUndefined()
|
||||
expect((yield* catalog.model.get(ProviderV2.ID.openai, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.openai)).request.body.apiKey).toBeUndefined()
|
||||
expect(required(yield* catalog.model.get(ProviderV2.ID.openai, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -191,26 +187,25 @@ describe("OpencodePlugin", () => {
|
|||
const catalog = yield* Catalog.Service
|
||||
const providerID = ProviderV2.ID.opencode
|
||||
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(providerID, () => {})
|
||||
catalog.model.update(providerID, ModelV2.ID.make("cheap-mini"), (model) => {
|
||||
model.capabilities.input = ["text"]
|
||||
model.capabilities.output = ["text"]
|
||||
model.cost = [...cost(1, 1)]
|
||||
model.time.released = DateTime.makeUnsafe(Date.now())
|
||||
model.time.released = Date.now()
|
||||
})
|
||||
catalog.model.update(providerID, ModelV2.ID.make("gpt-5-nano"), (model) => {
|
||||
model.capabilities.input = ["text"]
|
||||
model.capabilities.output = ["text"]
|
||||
model.cost = [...cost(10, 10)]
|
||||
model.time.released = DateTime.makeUnsafe(Date.now())
|
||||
model.time.released = Date.now()
|
||||
})
|
||||
})
|
||||
|
||||
const selected = yield* catalog.model.small(providerID)
|
||||
|
||||
expect(Option.getOrUndefined(selected)?.id).toBe(ModelV2.ID.make("gpt-5-nano"))
|
||||
expect(selected?.id).toBe(ModelV2.ID.make("gpt-5-nano"))
|
||||
}).pipe(
|
||||
Effect.provide(Catalog.locationLayer.pipe(Layer.provide(EventV2.defaultLayer), Layer.provide(locationLayer))),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { PluginV2 } from "@opencode-ai/core/plugin"
|
|||
import { ProviderPlugins } from "@opencode-ai/core/plugin/provider"
|
||||
import { OpenRouterPlugin } from "@opencode-ai/core/plugin/provider/openrouter"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { expectPluginRegistered, it, model, provider } from "./provider-helper"
|
||||
import { addPlugin, expectPluginRegistered, it, model, provider, required } from "./provider-helper"
|
||||
|
||||
describe("OpenRouterPlugin", () => {
|
||||
it.effect("is registered so legacy OpenRouter behavior can be applied", () =>
|
||||
|
|
@ -22,9 +22,8 @@ describe("OpenRouterPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(OpenRouterPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, OpenRouterPlugin)
|
||||
yield* catalog.transform((catalog) => {
|
||||
const openrouter = provider("openrouter", {
|
||||
api: { type: "aisdk", package: "@openrouter/ai-sdk-provider" },
|
||||
request: { headers: { Existing: "value" }, body: {} },
|
||||
|
|
@ -36,19 +35,19 @@ describe("OpenRouterPlugin", () => {
|
|||
catalog.provider.update(ProviderV2.ID.make("nvidia"), () => {})
|
||||
})
|
||||
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("openrouter"))).request.headers).toEqual({
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("openrouter"))).request.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(required(yield* catalog.provider.get(ProviderV2.ID.make("nvidia"))).request.headers).toEqual({})
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("creates an SDK only for the OpenRouter package", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(OpenRouterPlugin)
|
||||
yield* addPlugin(plugin, OpenRouterPlugin)
|
||||
|
||||
const ignored = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
|
|
@ -74,9 +73,8 @@ describe("OpenRouterPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(OpenRouterPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, OpenRouterPlugin)
|
||||
yield* catalog.transform((catalog) => {
|
||||
const openrouter = provider("openrouter", {
|
||||
api: { type: "aisdk", package: "@openrouter/ai-sdk-provider" },
|
||||
})
|
||||
|
|
@ -94,12 +92,12 @@ describe("OpenRouterPlugin", () => {
|
|||
})
|
||||
|
||||
expect(
|
||||
(yield* catalog.model.get(ProviderV2.ID.make("openrouter"), ModelV2.ID.make("openai/gpt-5-chat"))).enabled,
|
||||
required(yield* catalog.model.get(ProviderV2.ID.make("openrouter"), ModelV2.ID.make("openai/gpt-5-chat"))).enabled,
|
||||
).toBe(false)
|
||||
expect(
|
||||
(yield* catalog.model.get(ProviderV2.ID.make("openrouter"), ModelV2.ID.make("openai/gpt-5"))).enabled,
|
||||
required(yield* catalog.model.get(ProviderV2.ID.make("openrouter"), ModelV2.ID.make("openai/gpt-5"))).enabled,
|
||||
).toBe(true)
|
||||
expect((yield* catalog.model.get(ProviderV2.ID.openai, ModelV2.ID.make("openai/gpt-5-chat"))).enabled).toBe(true)
|
||||
expect(required(yield* catalog.model.get(ProviderV2.ID.openai, ModelV2.ID.make("openai/gpt-5-chat"))).enabled).toBe(true)
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -107,14 +105,13 @@ describe("OpenRouterPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(OpenRouterPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, OpenRouterPlugin)
|
||||
yield* catalog.transform((catalog) => {
|
||||
catalog.provider.update(ProviderV2.ID.make("custom-openrouter"), () => {})
|
||||
catalog.model.update(ProviderV2.ID.make("custom-openrouter"), ModelV2.ID.make("gpt-5-chat-latest"), () => {})
|
||||
})
|
||||
expect(
|
||||
(yield* catalog.model.get(ProviderV2.ID.make("custom-openrouter"), ModelV2.ID.make("gpt-5-chat-latest")))
|
||||
required(yield* catalog.model.get(ProviderV2.ID.make("custom-openrouter"), ModelV2.ID.make("gpt-5-chat-latest")))
|
||||
.enabled,
|
||||
).toBe(true)
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ import { Effect } from "effect"
|
|||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { PerplexityPlugin } from "@opencode-ai/core/plugin/provider/perplexity"
|
||||
import { fakeSelectorSdk, it, model } from "./provider-helper"
|
||||
import { addPlugin, fakeSelectorSdk, it, model } from "./provider-helper"
|
||||
|
||||
describe("PerplexityPlugin", () => {
|
||||
it.effect("creates a Perplexity SDK for the exact @ai-sdk/perplexity package", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(PerplexityPlugin)
|
||||
yield* addPlugin(plugin, PerplexityPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{ model: model("perplexity", "sonar"), package: "@ai-sdk/perplexity", options: { name: "perplexity" } },
|
||||
|
|
@ -22,7 +22,7 @@ describe("PerplexityPlugin", () => {
|
|||
it.effect("ignores packages that are not the bundled Perplexity package", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(PerplexityPlugin)
|
||||
yield* addPlugin(plugin, PerplexityPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -40,7 +40,7 @@ describe("PerplexityPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const providers: string[] = []
|
||||
yield* plugin.add(PerplexityPlugin)
|
||||
yield* addPlugin(plugin, PerplexityPlugin)
|
||||
yield* plugin.add({
|
||||
id: PluginV2.ID.make("perplexity-sdk-inspector"),
|
||||
effect: Effect.succeed({
|
||||
|
|
@ -63,7 +63,7 @@ describe("PerplexityPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const providers: string[] = []
|
||||
yield* plugin.add(PerplexityPlugin)
|
||||
yield* addPlugin(plugin, PerplexityPlugin)
|
||||
yield* plugin.add({
|
||||
id: PluginV2.ID.make("custom-perplexity-sdk-inspector"),
|
||||
effect: Effect.succeed({
|
||||
|
|
@ -90,7 +90,7 @@ describe("PerplexityPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(PerplexityPlugin)
|
||||
yield* addPlugin(plugin, PerplexityPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
import { describe, expect } from "bun:test"
|
||||
import { Effect } from "effect"
|
||||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { Npm } from "@opencode-ai/core/npm"
|
||||
import { SapAICorePlugin } from "@opencode-ai/core/plugin/provider/sap-ai-core"
|
||||
import { fixtureProvider, it, model, npmLayer, withEnv } from "./provider-helper"
|
||||
import { host } from "./host"
|
||||
|
||||
const pluginWithNpm = { id: SapAICorePlugin.id, effect: SapAICorePlugin.effect.pipe(Effect.provide(npmLayer)) }
|
||||
const pluginWithNpm = {
|
||||
id: SapAICorePlugin.id,
|
||||
effect: Effect.gen(function* () {
|
||||
yield* SapAICorePlugin.effect(host({ npm: yield* Npm.Service }))
|
||||
}).pipe(Effect.provide(npmLayer)),
|
||||
}
|
||||
|
||||
describe("SapAICorePlugin", () => {
|
||||
it.effect("copies serviceKey option into AICORE_SERVICE_KEY but keeps SDK options to deployment metadata", () =>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { Effect } from "effect"
|
|||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { SnowflakeCortexPlugin, cortexFetch } from "@opencode-ai/core/plugin/provider/snowflake-cortex"
|
||||
import { ProviderPlugins } from "@opencode-ai/core/plugin/provider"
|
||||
import { expectPluginRegistered, it, model, withEnv } from "./provider-helper"
|
||||
import { addPlugin, expectPluginRegistered, it, model, withEnv } from "./provider-helper"
|
||||
|
||||
describe("SnowflakeCortexPlugin", () => {
|
||||
it.effect("is registered in ProviderPlugins before OpenAICompatiblePlugin", () =>
|
||||
|
|
@ -20,7 +20,7 @@ describe("SnowflakeCortexPlugin", () => {
|
|||
it.effect("ignores non-snowflake-cortex providers", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(SnowflakeCortexPlugin)
|
||||
yield* addPlugin(plugin, SnowflakeCortexPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{ model: model("openai", "gpt-4"), package: "@ai-sdk/openai", options: { name: "openai" } },
|
||||
|
|
@ -34,7 +34,7 @@ describe("SnowflakeCortexPlugin", () => {
|
|||
withEnv({ SNOWFLAKE_CORTEX_PAT: "test-pat" }, () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(SnowflakeCortexPlugin)
|
||||
yield* addPlugin(plugin, SnowflakeCortexPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -53,7 +53,7 @@ describe("SnowflakeCortexPlugin", () => {
|
|||
withEnv({ SNOWFLAKE_CORTEX_PAT: undefined }, () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(SnowflakeCortexPlugin)
|
||||
yield* addPlugin(plugin, SnowflakeCortexPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -76,7 +76,7 @@ describe("SnowflakeCortexPlugin", () => {
|
|||
withEnv({ SNOWFLAKE_CORTEX_TOKEN: "oauth-token", SNOWFLAKE_CORTEX_PAT: undefined }, () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(SnowflakeCortexPlugin)
|
||||
yield* addPlugin(plugin, SnowflakeCortexPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -95,7 +95,7 @@ describe("SnowflakeCortexPlugin", () => {
|
|||
withEnv({ SNOWFLAKE_CORTEX_TOKEN: undefined, SNOWFLAKE_CORTEX_PAT: undefined }, () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(SnowflakeCortexPlugin)
|
||||
yield* addPlugin(plugin, SnowflakeCortexPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -119,7 +119,7 @@ describe("SnowflakeCortexPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const captured: Record<string, unknown>[] = []
|
||||
yield* plugin.add(SnowflakeCortexPlugin)
|
||||
yield* addPlugin(plugin, SnowflakeCortexPlugin)
|
||||
yield* plugin.add({
|
||||
id: PluginV2.ID.make("inspector"),
|
||||
effect: Effect.succeed({
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ import { describe, expect } from "bun:test"
|
|||
import { Effect } from "effect"
|
||||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { TogetherAIPlugin } from "@opencode-ai/core/plugin/provider/togetherai"
|
||||
import { fakeSelectorSdk, it, model } from "./provider-helper"
|
||||
import { addPlugin, fakeSelectorSdk, it, model } from "./provider-helper"
|
||||
|
||||
describe("TogetherAIPlugin", () => {
|
||||
it.effect("creates a TogetherAI SDK for @ai-sdk/togetherai", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(TogetherAIPlugin)
|
||||
yield* addPlugin(plugin, TogetherAIPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{ model: model("togetherai", "model"), package: "@ai-sdk/togetherai", options: { name: "togetherai" } },
|
||||
|
|
@ -21,7 +21,7 @@ describe("TogetherAIPlugin", () => {
|
|||
it.effect("matches the old bundled provider package exactly", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(TogetherAIPlugin)
|
||||
yield* addPlugin(plugin, TogetherAIPlugin)
|
||||
|
||||
const ignored = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
|
|
@ -47,7 +47,7 @@ describe("TogetherAIPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const observed: string[] = []
|
||||
yield* plugin.add(TogetherAIPlugin)
|
||||
yield* addPlugin(plugin, TogetherAIPlugin)
|
||||
yield* plugin.add({
|
||||
id: PluginV2.ID.make("inspector"),
|
||||
effect: Effect.succeed({
|
||||
|
|
@ -76,7 +76,7 @@ describe("TogetherAIPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(TogetherAIPlugin)
|
||||
yield* addPlugin(plugin, TogetherAIPlugin)
|
||||
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ import { describe, expect } from "bun:test"
|
|||
import { Effect } from "effect"
|
||||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { VenicePlugin } from "@opencode-ai/core/plugin/provider/venice"
|
||||
import { fakeSelectorSdk, it, model } from "./provider-helper"
|
||||
import { addPlugin, fakeSelectorSdk, it, model } from "./provider-helper"
|
||||
|
||||
describe("VenicePlugin", () => {
|
||||
it.effect("creates a Venice SDK for venice-ai-sdk-provider", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(VenicePlugin)
|
||||
yield* addPlugin(plugin, VenicePlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{ model: model("venice", "model"), package: "venice-ai-sdk-provider", options: { name: "venice" } },
|
||||
|
|
@ -22,7 +22,7 @@ describe("VenicePlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const observed: string[] = []
|
||||
yield* plugin.add(VenicePlugin)
|
||||
yield* addPlugin(plugin, VenicePlugin)
|
||||
yield* plugin.add({
|
||||
id: PluginV2.ID.make("inspector"),
|
||||
effect: Effect.succeed({
|
||||
|
|
@ -49,7 +49,7 @@ describe("VenicePlugin", () => {
|
|||
it.effect("only handles the bundled venice-ai-sdk-provider package", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(VenicePlugin)
|
||||
yield* addPlugin(plugin, VenicePlugin)
|
||||
const similar = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{
|
||||
|
|
@ -73,7 +73,7 @@ describe("VenicePlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
yield* plugin.add(VenicePlugin)
|
||||
yield* addPlugin(plugin, VenicePlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{ model: model("venice", "alias"), sdk: fakeSelectorSdk(calls), options: {} },
|
||||
|
|
|
|||
|
|
@ -4,16 +4,15 @@ import { Catalog } from "@opencode-ai/core/catalog"
|
|||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||
import { VercelPlugin } from "@opencode-ai/core/plugin/provider/vercel"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { it, model, provider } from "./provider-helper"
|
||||
import { addPlugin, it, model, provider, required } from "./provider-helper"
|
||||
|
||||
describe("VercelPlugin", () => {
|
||||
it.effect("applies legacy lower-case referer headers", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(VercelPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, VercelPlugin)
|
||||
yield* catalog.transform((catalog) => {
|
||||
const item = provider("vercel", {
|
||||
api: { type: "aisdk", package: "@ai-sdk/vercel" },
|
||||
request: { headers: { Existing: "1" }, body: {} },
|
||||
|
|
@ -23,7 +22,7 @@ describe("VercelPlugin", () => {
|
|||
draft.request = item.request
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("vercel"))).request.headers).toEqual({
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("vercel"))).request.headers).toEqual({
|
||||
Existing: "1",
|
||||
"http-referer": "https://opencode.ai/",
|
||||
"x-title": "opencode",
|
||||
|
|
@ -35,25 +34,24 @@ describe("VercelPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(VercelPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, VercelPlugin)
|
||||
yield* catalog.transform((catalog) => {
|
||||
const item = provider("vercel", { api: { type: "aisdk", package: "@ai-sdk/vercel" } })
|
||||
catalog.provider.update(item.id, (draft) => {
|
||||
draft.api = item.api
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("vercel"))).request.headers).not.toHaveProperty(
|
||||
expect(required(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(required(yield* catalog.provider.get(ProviderV2.ID.make("vercel"))).request.headers).not.toHaveProperty("X-Title")
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("creates @ai-sdk/vercel SDKs for custom provider IDs", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(VercelPlugin)
|
||||
yield* addPlugin(plugin, VercelPlugin)
|
||||
const event = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
{ model: model("custom-vercel", "v0-1.0-md"), package: "@ai-sdk/vercel", options: { name: "custom-vercel" } },
|
||||
|
|
@ -68,10 +66,9 @@ describe("VercelPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(VercelPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => catalog.provider.update(provider("gateway").id, () => {}))
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("gateway"))).request.headers).toEqual({})
|
||||
yield* addPlugin(plugin, VercelPlugin)
|
||||
yield* catalog.transform((catalog) => catalog.provider.update(provider("gateway").id, () => {}))
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("gateway"))).request.headers).toEqual({})
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { PluginV2 } from "@opencode-ai/core/plugin"
|
|||
import { XAIPlugin } from "@opencode-ai/core/plugin/provider/xai"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { testEffect } from "../lib/effect"
|
||||
import { fakeSelectorSdk } from "./provider-helper"
|
||||
import { addPlugin, fakeSelectorSdk } from "./provider-helper"
|
||||
|
||||
const it = testEffect(PluginV2.locationLayer.pipe(Layer.provide(EventV2.defaultLayer)))
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ describe("XAIPlugin", () => {
|
|||
it.effect("creates an xAI SDK only for @ai-sdk/xai", () =>
|
||||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
yield* plugin.add(XAIPlugin)
|
||||
yield* addPlugin(plugin, XAIPlugin)
|
||||
|
||||
const ignored = yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
|
|
@ -43,20 +43,18 @@ describe("XAIPlugin", () => {
|
|||
const plugin = yield* PluginV2.Service
|
||||
const providers: string[] = []
|
||||
|
||||
yield* plugin.add(XAIPlugin)
|
||||
yield* plugin.add(
|
||||
PluginV2.define({
|
||||
id: PluginV2.ID.make("xai-sdk-name-observer"),
|
||||
effect: Effect.gen(function* () {
|
||||
return {
|
||||
"aisdk.sdk": Effect.fn(function* (evt) {
|
||||
if (!evt.sdk) return
|
||||
providers.push(evt.sdk.responses("grok-4").provider)
|
||||
}),
|
||||
}
|
||||
}),
|
||||
yield* addPlugin(plugin, XAIPlugin)
|
||||
yield* plugin.add({
|
||||
id: PluginV2.ID.make("xai-sdk-name-observer"),
|
||||
effect: Effect.gen(function* () {
|
||||
return {
|
||||
"aisdk.sdk": Effect.fn(function* (evt) {
|
||||
if (!evt.sdk) return
|
||||
providers.push(evt.sdk.responses("grok-4").provider)
|
||||
}),
|
||||
}
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
yield* plugin.trigger(
|
||||
"aisdk.sdk",
|
||||
|
|
@ -77,7 +75,7 @@ describe("XAIPlugin", () => {
|
|||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
|
||||
yield* plugin.add(XAIPlugin)
|
||||
yield* addPlugin(plugin, XAIPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
@ -98,7 +96,7 @@ describe("XAIPlugin", () => {
|
|||
const plugin = yield* PluginV2.Service
|
||||
const calls: string[] = []
|
||||
|
||||
yield* plugin.add(XAIPlugin)
|
||||
yield* addPlugin(plugin, XAIPlugin)
|
||||
const result = yield* plugin.trigger(
|
||||
"aisdk.language",
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { PluginV2 } from "@opencode-ai/core/plugin"
|
|||
import { ProviderPlugins } from "@opencode-ai/core/plugin/provider"
|
||||
import { ZenmuxPlugin } from "@opencode-ai/core/plugin/provider/zenmux"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { expectPluginRegistered, it, provider } from "./provider-helper"
|
||||
import { addPlugin, expectPluginRegistered, it, provider, required } from "./provider-helper"
|
||||
|
||||
describe("ZenmuxPlugin", () => {
|
||||
it.effect("is registered so legacy referer headers can be applied", () =>
|
||||
|
|
@ -21,9 +21,8 @@ describe("ZenmuxPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(ZenmuxPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, ZenmuxPlugin)
|
||||
yield* catalog.transform((catalog) => {
|
||||
const item = provider("zenmux", {
|
||||
api: { type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://zenmux.ai/api/v1" },
|
||||
})
|
||||
|
|
@ -31,7 +30,7 @@ describe("ZenmuxPlugin", () => {
|
|||
draft.api = item.api
|
||||
})
|
||||
})
|
||||
const result = yield* catalog.provider.get(ProviderV2.ID.make("zenmux"))
|
||||
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"])
|
||||
}),
|
||||
|
|
@ -41,9 +40,8 @@ describe("ZenmuxPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(ZenmuxPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, ZenmuxPlugin)
|
||||
yield* catalog.transform((catalog) => {
|
||||
const item = provider("zenmux", {
|
||||
api: { type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://zenmux.ai/api/v1" },
|
||||
request: { headers: { Existing: "value" }, body: {} },
|
||||
|
|
@ -54,7 +52,7 @@ describe("ZenmuxPlugin", () => {
|
|||
})
|
||||
})
|
||||
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("zenmux"))).request.headers).toEqual({
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("zenmux"))).request.headers).toEqual({
|
||||
Existing: "value",
|
||||
"HTTP-Referer": "https://opencode.ai/",
|
||||
"X-Title": "opencode",
|
||||
|
|
@ -66,9 +64,8 @@ describe("ZenmuxPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(ZenmuxPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, ZenmuxPlugin)
|
||||
yield* catalog.transform((catalog) => {
|
||||
const item = provider("zenmux", {
|
||||
api: { type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://zenmux.ai/api/v1" },
|
||||
request: {
|
||||
|
|
@ -82,7 +79,7 @@ describe("ZenmuxPlugin", () => {
|
|||
})
|
||||
})
|
||||
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("zenmux"))).request.headers).toEqual({
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("zenmux"))).request.headers).toEqual({
|
||||
"HTTP-Referer": "https://example.com/",
|
||||
"X-Title": "custom-title",
|
||||
})
|
||||
|
|
@ -93,9 +90,8 @@ describe("ZenmuxPlugin", () => {
|
|||
Effect.gen(function* () {
|
||||
const plugin = yield* PluginV2.Service
|
||||
const catalog = yield* Catalog.Service
|
||||
yield* plugin.add(ZenmuxPlugin)
|
||||
const transform = yield* catalog.transform()
|
||||
yield* transform((catalog) => {
|
||||
yield* addPlugin(plugin, ZenmuxPlugin)
|
||||
yield* catalog.transform((catalog) => {
|
||||
const item = provider("openrouter", {
|
||||
request: {
|
||||
headers: { "HTTP-Referer": "https://example.com/", "X-Title": "custom-title" },
|
||||
|
|
@ -107,7 +103,7 @@ describe("ZenmuxPlugin", () => {
|
|||
})
|
||||
})
|
||||
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.openrouter)).request.headers).toEqual({
|
||||
expect(required(yield* catalog.provider.get(ProviderV2.ID.openrouter)).request.headers).toEqual({
|
||||
"HTTP-Referer": "https://example.com/",
|
||||
"X-Title": "custom-title",
|
||||
})
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { SkillPlugin } from "@opencode-ai/core/plugin/skill"
|
|||
import { SkillV2 } from "@opencode-ai/core/skill"
|
||||
import { SkillDiscovery } from "@opencode-ai/core/skill/discovery"
|
||||
import { testEffect } from "../lib/effect"
|
||||
import { host } from "./host"
|
||||
|
||||
const it = testEffect(
|
||||
SkillV2.layer.pipe(
|
||||
|
|
@ -19,7 +20,7 @@ describe("SkillPlugin.Plugin", () => {
|
|||
it.effect("registers the built-in customize-opencode skill", () =>
|
||||
Effect.gen(function* () {
|
||||
const skill = yield* SkillV2.Service
|
||||
yield* SkillPlugin.Plugin.effect.pipe(Effect.provideService(SkillV2.Service, skill))
|
||||
yield* SkillPlugin.Plugin.effect(host({ skill }))
|
||||
|
||||
expect(yield* skill.list()).toContainEqual(
|
||||
expect.objectContaining({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue