fix(provider): type model not found errors (#27334)
This commit is contained in:
@@ -21,6 +21,7 @@ import { pathToFileURL } from "url"
|
||||
import { Effect, Layer, Context, Schema, Types } from "effect"
|
||||
import { EffectBridge } from "@/effect/bridge"
|
||||
import { InstanceState } from "@/effect/instance-state"
|
||||
import { EffectPromise } from "@/effect/promise"
|
||||
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
||||
import { isRecord } from "@/util/record"
|
||||
import { optionalOmitUndefined } from "@opencode-ai/core/schema"
|
||||
@@ -963,11 +964,24 @@ export function defaultModelIDs<T extends { models: Record<string, { id: string
|
||||
return mapValues(providers, (item) => sort(Object.values(item.models))[0].id)
|
||||
}
|
||||
|
||||
export class ModelNotFoundError extends Schema.TaggedErrorClass<ModelNotFoundError>()("ProviderModelNotFoundError", {
|
||||
providerID: ProviderID,
|
||||
modelID: ModelID,
|
||||
suggestions: Schema.optional(Schema.Array(Schema.String)),
|
||||
cause: Schema.optional(Schema.Defect),
|
||||
}) {
|
||||
static isInstance(input: unknown): input is ModelNotFoundError {
|
||||
return input instanceof ModelNotFoundError
|
||||
}
|
||||
}
|
||||
|
||||
export type Error = ModelNotFoundError
|
||||
|
||||
export interface Interface {
|
||||
readonly list: () => Effect.Effect<Record<ProviderID, Info>>
|
||||
readonly getProvider: (providerID: ProviderID) => Effect.Effect<Info>
|
||||
readonly getModel: (providerID: ProviderID, modelID: ModelID) => Effect.Effect<Model>
|
||||
readonly getLanguage: (model: Model) => Effect.Effect<LanguageModelV3>
|
||||
readonly getModel: (providerID: ProviderID, modelID: ModelID) => Effect.Effect<Model, ModelNotFoundError>
|
||||
readonly getLanguage: (model: Model) => Effect.Effect<LanguageModelV3, ModelNotFoundError>
|
||||
readonly closest: (
|
||||
providerID: ProviderID,
|
||||
query: string[],
|
||||
@@ -1638,14 +1652,14 @@ const layer = Layer.effect(
|
||||
: fuzzysort
|
||||
.go(providerID, Object.keys({ ...s.catalog, ...s.providers }), { limit: 3, threshold: -10000 })
|
||||
.map((m) => m.target)
|
||||
throw new ModelNotFoundError({ providerID, modelID, suggestions })
|
||||
return yield* new ModelNotFoundError({ providerID, modelID, suggestions })
|
||||
}
|
||||
|
||||
const info = provider.models[modelID]
|
||||
if (!info) {
|
||||
const current = modelSuggestions(provider, modelID)
|
||||
const suggestions = current.length ? current : modelSuggestions(s.catalog[providerID], modelID)
|
||||
throw new ModelNotFoundError({ providerID, modelID, suggestions })
|
||||
return yield* new ModelNotFoundError({ providerID, modelID, suggestions })
|
||||
}
|
||||
return info
|
||||
})
|
||||
@@ -1656,11 +1670,10 @@ const layer = Layer.effect(
|
||||
const key = `${model.providerID}/${model.id}`
|
||||
if (s.models.has(key)) return s.models.get(key)!
|
||||
|
||||
return yield* Effect.promise(async () => {
|
||||
const provider = s.providers[model.providerID]
|
||||
const sdk = await resolveSDK(model, s, envs)
|
||||
|
||||
try {
|
||||
const provider = s.providers[model.providerID]
|
||||
return yield* EffectPromise.refineRejection(
|
||||
async () => {
|
||||
const sdk = await resolveSDK(model, s, envs)
|
||||
const language = s.modelLoaders[model.providerID]
|
||||
? await s.modelLoaders[model.providerID](sdk, model.api.id, {
|
||||
...provider.options,
|
||||
@@ -1669,18 +1682,12 @@ const layer = Layer.effect(
|
||||
: sdk.languageModel(model.api.id)
|
||||
s.models.set(key, language)
|
||||
return language
|
||||
} catch (e) {
|
||||
if (e instanceof NoSuchModelError)
|
||||
throw new ModelNotFoundError(
|
||||
{
|
||||
modelID: model.id,
|
||||
providerID: model.providerID,
|
||||
},
|
||||
{ cause: e },
|
||||
)
|
||||
throw e
|
||||
}
|
||||
})
|
||||
},
|
||||
(cause) =>
|
||||
cause instanceof NoSuchModelError
|
||||
? new ModelNotFoundError({ modelID: model.id, providerID: model.providerID, cause })
|
||||
: undefined,
|
||||
)
|
||||
})
|
||||
|
||||
const closest = Effect.fn("Provider.closest")(function* (providerID: ProviderID, query: string[]) {
|
||||
@@ -1700,7 +1707,7 @@ const layer = Layer.effect(
|
||||
|
||||
if (cfg.small_model) {
|
||||
const parsed = parseModel(cfg.small_model)
|
||||
return yield* getModel(parsed.providerID, parsed.modelID)
|
||||
return yield* getModel(parsed.providerID, parsed.modelID).pipe(Effect.orDie)
|
||||
}
|
||||
|
||||
const s = yield* InstanceState.get(state)
|
||||
@@ -1728,22 +1735,22 @@ const layer = Layer.effect(
|
||||
const candidates = Object.keys(provider.models).filter((m) => m.includes(item))
|
||||
|
||||
const globalMatch = candidates.find((m) => m.startsWith("global."))
|
||||
if (globalMatch) return yield* getModel(providerID, ModelID.make(globalMatch))
|
||||
if (globalMatch) return yield* getModel(providerID, ModelID.make(globalMatch)).pipe(Effect.orDie)
|
||||
|
||||
const region = provider.options?.region
|
||||
if (region) {
|
||||
const regionPrefix = region.split("-")[0]
|
||||
if (regionPrefix === "us" || regionPrefix === "eu") {
|
||||
const regionalMatch = candidates.find((m) => m.startsWith(`${regionPrefix}.`))
|
||||
if (regionalMatch) return yield* getModel(providerID, ModelID.make(regionalMatch))
|
||||
if (regionalMatch) return yield* getModel(providerID, ModelID.make(regionalMatch)).pipe(Effect.orDie)
|
||||
}
|
||||
}
|
||||
|
||||
const unprefixed = candidates.find((m) => !crossRegionPrefixes.some((p) => m.startsWith(p)))
|
||||
if (unprefixed) return yield* getModel(providerID, ModelID.make(unprefixed))
|
||||
if (unprefixed) return yield* getModel(providerID, ModelID.make(unprefixed)).pipe(Effect.orDie)
|
||||
} else {
|
||||
for (const model of Object.keys(provider.models)) {
|
||||
if (model.includes(item)) return yield* getModel(providerID, ModelID.make(model))
|
||||
if (model.includes(item)) return yield* getModel(providerID, ModelID.make(model)).pipe(Effect.orDie)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1818,12 +1825,6 @@ export function parseModel(model: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export const ModelNotFoundError = NamedError.create("ProviderModelNotFoundError", {
|
||||
providerID: ProviderID,
|
||||
modelID: ModelID,
|
||||
suggestions: Schema.optional(Schema.Array(Schema.String)),
|
||||
})
|
||||
|
||||
export const InitError = NamedError.create("ProviderInitError", {
|
||||
providerID: ProviderID,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user