fix(core): load documented provider packages
This commit is contained in:
parent
f019bb3abf
commit
4b66e5484d
3 changed files with 109 additions and 0 deletions
|
|
@ -53,6 +53,9 @@ export const Plugin = define({
|
||||||
catalog.provider.update(providerID, (provider) => {
|
catalog.provider.update(providerID, (provider) => {
|
||||||
if (item.name !== undefined) provider.name = item.name
|
if (item.name !== undefined) provider.name = item.name
|
||||||
if (item.api !== undefined) provider.api = { ...item.api }
|
if (item.api !== undefined) provider.api = { ...item.api }
|
||||||
|
if (item.package !== undefined || item.settings !== undefined) {
|
||||||
|
provider.api = configuredApi(provider.api, item.package, item.settings)
|
||||||
|
}
|
||||||
if (item.request !== undefined) {
|
if (item.request !== undefined) {
|
||||||
Object.assign(provider.request.headers, item.request.headers)
|
Object.assign(provider.request.headers, item.request.headers)
|
||||||
Object.assign(provider.request.body, item.request.body)
|
Object.assign(provider.request.body, item.request.body)
|
||||||
|
|
@ -63,6 +66,16 @@ export const Plugin = define({
|
||||||
if (config.family !== undefined) model.family = config.family
|
if (config.family !== undefined) model.family = config.family
|
||||||
if (config.name !== undefined) model.name = config.name
|
if (config.name !== undefined) model.name = config.name
|
||||||
if (config.api !== undefined) model.api = { ...model.api, ...config.api }
|
if (config.api !== undefined) model.api = { ...model.api, ...config.api }
|
||||||
|
if (config.package !== undefined || config.settings !== undefined) {
|
||||||
|
model.api = {
|
||||||
|
...configuredApi(
|
||||||
|
config.api === undefined ? catalog.provider.get(providerID)!.provider.api : model.api,
|
||||||
|
config.package,
|
||||||
|
config.settings,
|
||||||
|
),
|
||||||
|
id: model.api.id,
|
||||||
|
}
|
||||||
|
}
|
||||||
if (config.capabilities !== undefined) {
|
if (config.capabilities !== undefined) {
|
||||||
model.capabilities = {
|
model.capabilities = {
|
||||||
tools: config.capabilities.tools,
|
tools: config.capabilities.tools,
|
||||||
|
|
@ -111,3 +124,25 @@ export const Plugin = define({
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function configuredApi(api: ProviderV2.MutableApi, packageName?: string, settings?: Record<string, unknown>) {
|
||||||
|
const merged = { ...api.settings, ...settings }
|
||||||
|
const baseURL = typeof merged.baseURL === "string" ? merged.baseURL : api.url
|
||||||
|
if (packageName?.startsWith("aisdk:")) {
|
||||||
|
return {
|
||||||
|
type: "aisdk" as const,
|
||||||
|
package: packageName.slice("aisdk:".length),
|
||||||
|
...(baseURL === undefined ? {} : { url: baseURL }),
|
||||||
|
settings: merged,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (packageName !== undefined) {
|
||||||
|
return {
|
||||||
|
type: "native" as const,
|
||||||
|
package: packageName,
|
||||||
|
...(baseURL === undefined ? {} : { url: baseURL }),
|
||||||
|
settings: merged,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { ...api, ...(baseURL === undefined ? {} : { url: baseURL }), settings: merged }
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,8 @@ const ModelApi = Schema.Union([
|
||||||
class Model extends Schema.Class<Model>("ConfigV2.Model")({
|
class Model extends Schema.Class<Model>("ConfigV2.Model")({
|
||||||
family: ModelV2.Family.pipe(Schema.optional),
|
family: ModelV2.Family.pipe(Schema.optional),
|
||||||
name: Schema.String.pipe(Schema.optional),
|
name: Schema.String.pipe(Schema.optional),
|
||||||
|
package: Schema.String.pipe(Schema.optional),
|
||||||
|
settings: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
|
||||||
api: ModelApi.pipe(Schema.optional),
|
api: ModelApi.pipe(Schema.optional),
|
||||||
capabilities: ModelV2.Capabilities.pipe(Schema.optional),
|
capabilities: ModelV2.Capabilities.pipe(Schema.optional),
|
||||||
request: Schema.Struct({
|
request: Schema.Struct({
|
||||||
|
|
@ -65,6 +67,8 @@ class Model extends Schema.Class<Model>("ConfigV2.Model")({
|
||||||
export class Info extends Schema.Class<Info>("ConfigV2.Provider")({
|
export class Info extends Schema.Class<Info>("ConfigV2.Provider")({
|
||||||
name: Schema.String.pipe(Schema.optional),
|
name: Schema.String.pipe(Schema.optional),
|
||||||
env: Schema.String.pipe(Schema.Array, Schema.optional),
|
env: Schema.String.pipe(Schema.Array, Schema.optional),
|
||||||
|
package: Schema.String.pipe(Schema.optional),
|
||||||
|
settings: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
|
||||||
api: ProviderV2.Api.pipe(Schema.optional),
|
api: ProviderV2.Api.pipe(Schema.optional),
|
||||||
request: Request.pipe(Schema.optional),
|
request: Request.pipe(Schema.optional),
|
||||||
models: Schema.Record(Schema.String, Model).pipe(Schema.optional),
|
models: Schema.Record(Schema.String, Model).pipe(Schema.optional),
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,76 @@ function request(headers: Record<string, string>, variant?: string) {
|
||||||
const decode = Schema.decodeUnknownSync(Config.Info)
|
const decode = Schema.decodeUnknownSync(Config.Info)
|
||||||
|
|
||||||
describe("ConfigProviderPlugin.Plugin", () => {
|
describe("ConfigProviderPlugin.Plugin", () => {
|
||||||
|
it.effect("loads documented native provider packages", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const catalog = yield* Catalog.Service
|
||||||
|
const providerID = ProviderV2.ID.make("custom")
|
||||||
|
const modelID = ModelV2.ID.make("chat")
|
||||||
|
const config = Config.Service.of({
|
||||||
|
entries: () =>
|
||||||
|
Effect.succeed([
|
||||||
|
new Config.Document({
|
||||||
|
type: "document",
|
||||||
|
info: decode({
|
||||||
|
providers: {
|
||||||
|
custom: {
|
||||||
|
package: "@opencode-ai/llm/providers/openai-compatible",
|
||||||
|
settings: { baseURL: "https://example.test/v1" },
|
||||||
|
models: { chat: { name: "Chat" } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
|
||||||
|
yield* addPlugin(config)
|
||||||
|
|
||||||
|
expect(required(yield* catalog.model.get(providerID, modelID)).api).toEqual({
|
||||||
|
id: modelID,
|
||||||
|
type: "native",
|
||||||
|
package: "@opencode-ai/llm/providers/openai-compatible",
|
||||||
|
url: "https://example.test/v1",
|
||||||
|
settings: { baseURL: "https://example.test/v1" },
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
it.effect("loads documented aisdk provider packages", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const catalog = yield* Catalog.Service
|
||||||
|
const providerID = ProviderV2.ID.make("custom")
|
||||||
|
const modelID = ModelV2.ID.make("chat")
|
||||||
|
const config = Config.Service.of({
|
||||||
|
entries: () =>
|
||||||
|
Effect.succeed([
|
||||||
|
new Config.Document({
|
||||||
|
type: "document",
|
||||||
|
info: decode({
|
||||||
|
providers: {
|
||||||
|
custom: {
|
||||||
|
package: "aisdk:@ai-sdk/openai-compatible",
|
||||||
|
settings: { baseURL: "https://example.test/v1" },
|
||||||
|
models: { chat: { name: "Chat" } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
|
||||||
|
yield* addPlugin(config)
|
||||||
|
|
||||||
|
expect(required(yield* catalog.model.get(providerID, modelID)).api).toEqual({
|
||||||
|
id: modelID,
|
||||||
|
type: "aisdk",
|
||||||
|
package: "@ai-sdk/openai-compatible",
|
||||||
|
url: "https://example.test/v1",
|
||||||
|
settings: { baseURL: "https://example.test/v1" },
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
it.effect("keeps configured model variant bodies unchanged", () =>
|
it.effect("keeps configured model variant bodies unchanged", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const catalog = yield* Catalog.Service
|
const catalog = yield* Catalog.Service
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue