refactor(core): simplify provider helpers
This commit is contained in:
parent
3b63568da2
commit
9ad7612ff4
3 changed files with 66 additions and 50 deletions
|
|
@ -15,11 +15,12 @@ export const Plugin = define({
|
||||||
const generated = generate(draft, record.provider)
|
const generated = generate(draft, record.provider)
|
||||||
if (generated.length === 0) return
|
if (generated.length === 0) return
|
||||||
|
|
||||||
const explicit = new Map((draft.variants ?? []).map((variant) => [variant.id, variant]))
|
const variants = draft.variants ?? []
|
||||||
|
const explicit = new Map(variants.map((variant) => [variant.id, variant]))
|
||||||
const generatedIDs = new Set<string>(generated.map((variant) => variant.id))
|
const generatedIDs = new Set<string>(generated.map((variant) => variant.id))
|
||||||
draft.variants = [
|
draft.variants = [
|
||||||
...generated.map((variant) => explicit.get(variant.id) ?? variant),
|
...generated.map((variant) => explicit.get(variant.id) ?? variant),
|
||||||
...(draft.variants ?? []).filter((variant) => !generatedIDs.has(variant.id)),
|
...variants.filter((variant) => !generatedIDs.has(variant.id)),
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,12 @@ export const ID = Provider.ID
|
||||||
export type ID = typeof ID.Type
|
export type ID = typeof ID.Type
|
||||||
|
|
||||||
export const AISDK_PREFIX = "aisdk:"
|
export const AISDK_PREFIX = "aisdk:"
|
||||||
export const isAISDK = (packageName: string | undefined) => packageName?.startsWith(AISDK_PREFIX) ?? false
|
export const isAISDK = (value: string | undefined) => value?.startsWith(AISDK_PREFIX) ?? false
|
||||||
export const aisdk = (packageName: string) => (isAISDK(packageName) ? packageName : `${AISDK_PREFIX}${packageName}`)
|
export const aisdk = (value: string) => (isAISDK(value) ? value : `${AISDK_PREFIX}${value}`)
|
||||||
export const packageName = (packageName: string | undefined) =>
|
export const packageName = (value: string | undefined) => {
|
||||||
isAISDK(packageName) ? packageName!.slice(AISDK_PREFIX.length) : packageName
|
if (value === undefined || !isAISDK(value)) return value
|
||||||
|
return value.slice(AISDK_PREFIX.length)
|
||||||
|
}
|
||||||
|
|
||||||
export class LoadError extends Schema.TaggedErrorClass<LoadError>()("ProviderV2.LoadError", {
|
export class LoadError extends Schema.TaggedErrorClass<LoadError>()("ProviderV2.LoadError", {
|
||||||
package: Schema.String,
|
package: Schema.String,
|
||||||
|
|
@ -25,7 +27,14 @@ export type ProviderPackage = ProviderPackageDefinition
|
||||||
const packages = new Map<string, Promise<unknown>>()
|
const packages = new Map<string, Promise<unknown>>()
|
||||||
|
|
||||||
export const loadPackage = Effect.fn("ProviderV2.loadPackage")(function* (specifier: string, npm?: Npm.Interface) {
|
export const loadPackage = Effect.fn("ProviderV2.loadPackage")(function* (specifier: string, npm?: Npm.Interface) {
|
||||||
const resolved = yield* resolvePackage(specifier)
|
const resolved = yield* Effect.sync(() => {
|
||||||
|
if (specifier.startsWith("file://") || specifier.startsWith("@opencode-ai/llm/")) return specifier
|
||||||
|
try {
|
||||||
|
return import.meta.resolve(specifier)
|
||||||
|
} catch {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
})
|
||||||
if (resolved) return yield* importPackage(specifier, resolved)
|
if (resolved) return yield* importPackage(specifier, resolved)
|
||||||
if (!npm) {
|
if (!npm) {
|
||||||
return yield* new LoadError({
|
return yield* new LoadError({
|
||||||
|
|
@ -33,8 +42,10 @@ export const loadPackage = Effect.fn("ProviderV2.loadPackage")(function* (specif
|
||||||
cause: new Error(`Provider package ${specifier} is not installed`),
|
cause: new Error(`Provider package ${specifier} is not installed`),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
const parts = specifier.split("/")
|
||||||
|
const root = specifier.startsWith("@") ? parts.slice(0, 2).join("/") : (parts[0] ?? specifier)
|
||||||
const installed = yield* npm
|
const installed = yield* npm
|
||||||
.add(rootPackage(specifier))
|
.add(root)
|
||||||
.pipe(Effect.mapError((cause) => new LoadError({ package: specifier, cause })))
|
.pipe(Effect.mapError((cause) => new LoadError({ package: specifier, cause })))
|
||||||
const entrypoint = yield* Effect.try({
|
const entrypoint = yield* Effect.try({
|
||||||
try: () => import.meta.resolve(specifier, pathToFileURL(`${installed.directory}/`).href),
|
try: () => import.meta.resolve(specifier, pathToFileURL(`${installed.directory}/`).href),
|
||||||
|
|
@ -95,16 +106,6 @@ export type Info = Provider.Info
|
||||||
|
|
||||||
export type MutableInfo = DeepMutable<Info>
|
export type MutableInfo = DeepMutable<Info>
|
||||||
|
|
||||||
const resolvePackage = (specifier: string) =>
|
|
||||||
Effect.sync(() => {
|
|
||||||
if (specifier.startsWith("file://") || specifier.startsWith("@opencode-ai/llm/")) return specifier
|
|
||||||
try {
|
|
||||||
return import.meta.resolve(specifier)
|
|
||||||
} catch {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const importPackage = Effect.fn("ProviderV2.importPackage")(function* (specifier: string, entrypoint: string) {
|
const importPackage = Effect.fn("ProviderV2.importPackage")(function* (specifier: string, entrypoint: string) {
|
||||||
const module = yield* Effect.tryPromise({
|
const module = yield* Effect.tryPromise({
|
||||||
try: () => {
|
try: () => {
|
||||||
|
|
@ -116,21 +117,11 @@ const importPackage = Effect.fn("ProviderV2.importPackage")(function* (specifier
|
||||||
},
|
},
|
||||||
catch: (cause) => new LoadError({ package: specifier, cause }),
|
catch: (cause) => new LoadError({ package: specifier, cause }),
|
||||||
})
|
})
|
||||||
if (!isProviderPackage(module)) {
|
if (typeof module !== "object" || module === null || typeof (module as { model?: unknown }).model !== "function") {
|
||||||
return yield* new LoadError({
|
return yield* new LoadError({
|
||||||
package: specifier,
|
package: specifier,
|
||||||
cause: new Error(`Provider package ${specifier} does not export model(modelID, settings)`),
|
cause: new Error(`Provider package ${specifier} does not export model(modelID, settings)`),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return module
|
return module as ProviderPackageDefinition
|
||||||
})
|
})
|
||||||
|
|
||||||
function isProviderPackage(input: unknown): input is ProviderPackageDefinition {
|
|
||||||
return typeof input === "object" && input !== null && "model" in input && typeof input.model === "function"
|
|
||||||
}
|
|
||||||
|
|
||||||
function rootPackage(specifier: string) {
|
|
||||||
const parts = specifier.split("/")
|
|
||||||
if (specifier.startsWith("@")) return parts.slice(0, 2).join("/")
|
|
||||||
return parts[0]
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -108,19 +108,6 @@ export interface Dependencies {
|
||||||
readonly loadAISDK?: (model: ModelV2.Info) => Effect.Effect<Model, AISDK.InitError>
|
readonly loadAISDK?: (model: ModelV2.Info) => Effect.Effect<Model, AISDK.InitError>
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsupported = (model: ModelV2.Info, packageName = model.package ?? "unknown") =>
|
|
||||||
new UnsupportedPackageError({
|
|
||||||
providerID: model.providerID,
|
|
||||||
modelID: model.id,
|
|
||||||
package: packageName,
|
|
||||||
})
|
|
||||||
|
|
||||||
const credentialSettings = (credential: Credential.Value | undefined) => ({
|
|
||||||
...(credential?.type === "key" ? { apiKey: credential.key } : {}),
|
|
||||||
...(credential?.type === "oauth" ? { apiKey: credential.access } : {}),
|
|
||||||
...credential?.metadata,
|
|
||||||
})
|
|
||||||
|
|
||||||
export const fromCatalogModel = (
|
export const fromCatalogModel = (
|
||||||
model: ModelV2.Info,
|
model: ModelV2.Info,
|
||||||
credential?: Credential.Value,
|
credential?: Credential.Value,
|
||||||
|
|
@ -134,35 +121,72 @@ export const fromCatalogModel = (
|
||||||
})
|
})
|
||||||
if (ProviderV2.isAISDK(resolved.package)) {
|
if (ProviderV2.isAISDK(resolved.package)) {
|
||||||
if (!dependencies.loadAISDK) {
|
if (!dependencies.loadAISDK) {
|
||||||
return Effect.fail(unsupported(resolved))
|
return Effect.fail(
|
||||||
|
new UnsupportedPackageError({
|
||||||
|
providerID: resolved.providerID,
|
||||||
|
modelID: resolved.id,
|
||||||
|
package: resolved.package ?? "unknown",
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
const runtime = produce(resolved, (draft) => {
|
const runtime = produce(resolved, (draft) => {
|
||||||
draft.settings = ProviderV2.mergeOverlay(draft.settings, credentialSettings(credential))
|
draft.settings = ProviderV2.mergeOverlay(draft.settings, {
|
||||||
|
...(credential?.type === "key" ? { apiKey: credential.key } : {}),
|
||||||
|
...(credential?.type === "oauth" ? { apiKey: credential.access } : {}),
|
||||||
|
...credential?.metadata,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
return dependencies.loadAISDK(runtime).pipe(
|
return dependencies.loadAISDK(runtime).pipe(
|
||||||
Effect.mapError(() => unsupported(resolved)),
|
Effect.mapError(
|
||||||
|
() =>
|
||||||
|
new UnsupportedPackageError({
|
||||||
|
providerID: resolved.providerID,
|
||||||
|
modelID: resolved.id,
|
||||||
|
package: resolved.package ?? "unknown",
|
||||||
|
}),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (resolved.package) {
|
if (resolved.package) {
|
||||||
const specifier = resolved.package
|
const specifier = resolved.package
|
||||||
return Effect.gen(function* () {
|
return Effect.gen(function* () {
|
||||||
const module = yield* (dependencies.loadPackage ?? ProviderV2.loadPackage)(specifier).pipe(
|
const module = yield* (dependencies.loadPackage ?? ProviderV2.loadPackage)(specifier).pipe(
|
||||||
Effect.mapError(() => unsupported(resolved, specifier)),
|
Effect.mapError(
|
||||||
|
() =>
|
||||||
|
new UnsupportedPackageError({
|
||||||
|
providerID: resolved.providerID,
|
||||||
|
modelID: resolved.id,
|
||||||
|
package: specifier,
|
||||||
|
}),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
const settings = {
|
const settings = {
|
||||||
...resolved.settings,
|
...resolved.settings,
|
||||||
...credentialSettings(credential),
|
...(credential?.type === "key" ? { apiKey: credential.key } : {}),
|
||||||
|
...(credential?.type === "oauth" ? { apiKey: credential.access } : {}),
|
||||||
|
...credential?.metadata,
|
||||||
headers: resolved.headers,
|
headers: resolved.headers,
|
||||||
body: resolved.body,
|
body: resolved.body,
|
||||||
limits: { context: resolved.limit.context, output: resolved.limit.output },
|
limits: { context: resolved.limit.context, output: resolved.limit.output },
|
||||||
}
|
}
|
||||||
return yield* Effect.try({
|
return yield* Effect.try({
|
||||||
try: () => Model.update(module.model(resolved.modelID ?? resolved.id, settings), { provider: resolved.providerID }),
|
try: () => Model.update(module.model(resolved.modelID ?? resolved.id, settings), { provider: resolved.providerID }),
|
||||||
catch: () => unsupported(resolved, specifier),
|
catch: () =>
|
||||||
|
new UnsupportedPackageError({
|
||||||
|
providerID: resolved.providerID,
|
||||||
|
modelID: resolved.id,
|
||||||
|
package: specifier,
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return Effect.fail(unsupported(resolved))
|
return Effect.fail(
|
||||||
|
new UnsupportedPackageError({
|
||||||
|
providerID: resolved.providerID,
|
||||||
|
modelID: resolved.id,
|
||||||
|
package: resolved.package ?? "unknown",
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const resolve = (
|
export const resolve = (
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue