|
@@ -1,18 +1,22 @@
|
|
|
export * as Catalog from "./catalog"
|
|
export * as Catalog from "./catalog"
|
|
|
|
|
|
|
|
-import { Context, Effect, HashMap, Layer, Option, Order, pipe, Schema, Array, Scope, Stream } from "effect"
|
|
|
|
|
-import { produce, type Draft } from "immer"
|
|
|
|
|
|
|
+import { Context, Effect, Layer, Option, Order, pipe, Schema, Array, Scope, Stream } from "effect"
|
|
|
|
|
+import { castDraft, enableMapSet, type Draft } from "immer"
|
|
|
import { ModelV2 } from "./model"
|
|
import { ModelV2 } from "./model"
|
|
|
import { PluginV2 } from "./plugin"
|
|
import { PluginV2 } from "./plugin"
|
|
|
import { ProviderV2 } from "./provider"
|
|
import { ProviderV2 } from "./provider"
|
|
|
import { Location } from "./location"
|
|
import { Location } from "./location"
|
|
|
import { EventV2 } from "./event"
|
|
import { EventV2 } from "./event"
|
|
|
|
|
+import { Policy } from "./policy"
|
|
|
|
|
+import { State } from "./state"
|
|
|
|
|
|
|
|
export type ProviderRecord = {
|
|
export type ProviderRecord = {
|
|
|
provider: ProviderV2.Info
|
|
provider: ProviderV2.Info
|
|
|
models: Map<ModelV2.ID, ModelV2.Info>
|
|
models: Map<ModelV2.ID, ModelV2.Info>
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+export type DefaultModel = { providerID: ProviderV2.ID; modelID: ModelV2.ID }
|
|
|
|
|
+
|
|
|
export class ProviderNotFoundError extends Schema.TaggedErrorClass<ProviderNotFoundError>()(
|
|
export class ProviderNotFoundError extends Schema.TaggedErrorClass<ProviderNotFoundError>()(
|
|
|
"CatalogV2.ProviderNotFound",
|
|
"CatalogV2.ProviderNotFound",
|
|
|
{
|
|
{
|
|
@@ -25,6 +29,8 @@ export class ModelNotFoundError extends Schema.TaggedErrorClass<ModelNotFoundErr
|
|
|
modelID: ModelV2.ID,
|
|
modelID: ModelV2.ID,
|
|
|
}) {}
|
|
}) {}
|
|
|
|
|
|
|
|
|
|
+export const PolicyActions = Schema.Literals(["provider.use"])
|
|
|
|
|
+
|
|
|
export const Event = {
|
|
export const Event = {
|
|
|
ModelUpdated: EventV2.define({
|
|
ModelUpdated: EventV2.define({
|
|
|
type: "catalog.model.updated",
|
|
type: "catalog.model.updated",
|
|
@@ -34,24 +40,31 @@ export const Event = {
|
|
|
}),
|
|
}),
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-export type Context = {
|
|
|
|
|
- data: readonly ProviderRecord[]
|
|
|
|
|
- updateProvider: (providerID: ProviderV2.ID, fn: (provider: Draft<ProviderV2.Info>) => void) => void
|
|
|
|
|
- updateModel: (providerID: ProviderV2.ID, modelID: ModelV2.ID, fn: (model: Draft<ModelV2.Info>) => void) => void
|
|
|
|
|
|
|
+type Data = {
|
|
|
|
|
+ providers: Map<ProviderV2.ID, ProviderRecord>
|
|
|
|
|
+ defaultModel?: DefaultModel
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+export type Editor = {
|
|
|
provider: {
|
|
provider: {
|
|
|
|
|
+ list: () => readonly ProviderRecord[]
|
|
|
|
|
+ get: (providerID: ProviderV2.ID) => ProviderRecord | undefined
|
|
|
update: (providerID: ProviderV2.ID, fn: (provider: Draft<ProviderV2.Info>) => void) => void
|
|
update: (providerID: ProviderV2.ID, fn: (provider: Draft<ProviderV2.Info>) => void) => void
|
|
|
remove: (providerID: ProviderV2.ID) => void
|
|
remove: (providerID: ProviderV2.ID) => void
|
|
|
}
|
|
}
|
|
|
model: {
|
|
model: {
|
|
|
|
|
+ get: (providerID: ProviderV2.ID, modelID: ModelV2.ID) => ModelV2.Info | undefined
|
|
|
update: (providerID: ProviderV2.ID, modelID: ModelV2.ID, fn: (model: Draft<ModelV2.Info>) => void) => void
|
|
update: (providerID: ProviderV2.ID, modelID: ModelV2.ID, fn: (model: Draft<ModelV2.Info>) => void) => void
|
|
|
remove: (providerID: ProviderV2.ID, modelID: ModelV2.ID) => void
|
|
remove: (providerID: ProviderV2.ID, modelID: ModelV2.ID) => void
|
|
|
|
|
+ default: {
|
|
|
|
|
+ get: () => DefaultModel | undefined
|
|
|
|
|
+ set: (providerID: ProviderV2.ID, modelID: ModelV2.ID) => void
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-export type Loader = (update: (ctx: Context) => void) => Effect.Effect<void>
|
|
|
|
|
-
|
|
|
|
|
export interface Interface {
|
|
export interface Interface {
|
|
|
- readonly loader: () => Effect.Effect<Loader, never, Scope.Scope>
|
|
|
|
|
|
|
+ readonly transform: State.Interface<Data, Editor>["transform"]
|
|
|
readonly provider: {
|
|
readonly provider: {
|
|
|
readonly get: (providerID: ProviderV2.ID) => Effect.Effect<ProviderV2.Info, ProviderNotFoundError>
|
|
readonly get: (providerID: ProviderV2.ID) => Effect.Effect<ProviderV2.Info, ProviderNotFoundError>
|
|
|
readonly all: () => Effect.Effect<ProviderV2.Info[]>
|
|
readonly all: () => Effect.Effect<ProviderV2.Info[]>
|
|
@@ -65,29 +78,25 @@ export interface Interface {
|
|
|
readonly all: () => Effect.Effect<ModelV2.Info[]>
|
|
readonly all: () => Effect.Effect<ModelV2.Info[]>
|
|
|
readonly available: () => Effect.Effect<ModelV2.Info[]>
|
|
readonly available: () => Effect.Effect<ModelV2.Info[]>
|
|
|
readonly default: () => Effect.Effect<Option.Option<ModelV2.Info>>
|
|
readonly default: () => Effect.Effect<Option.Option<ModelV2.Info>>
|
|
|
- readonly setDefault: (
|
|
|
|
|
- providerID: ProviderV2.ID,
|
|
|
|
|
- modelID: ModelV2.ID,
|
|
|
|
|
- ) => Effect.Effect<void, ProviderNotFoundError | ModelNotFoundError>
|
|
|
|
|
readonly small: (providerID: ProviderV2.ID) => Effect.Effect<Option.Option<ModelV2.Info>>
|
|
readonly small: (providerID: ProviderV2.ID) => Effect.Effect<Option.Option<ModelV2.Info>>
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/Catalog") {}
|
|
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/Catalog") {}
|
|
|
|
|
|
|
|
|
|
+enableMapSet()
|
|
|
|
|
+
|
|
|
export const layer = Layer.effect(
|
|
export const layer = Layer.effect(
|
|
|
Service,
|
|
Service,
|
|
|
Effect.gen(function* () {
|
|
Effect.gen(function* () {
|
|
|
yield* Location.Service
|
|
yield* Location.Service
|
|
|
- let records = HashMap.empty<ProviderV2.ID, ProviderRecord>()
|
|
|
|
|
- let loaders: { update: (ctx: Context) => void }[] = []
|
|
|
|
|
- let defaultModel: { providerID: ProviderV2.ID; modelID: ModelV2.ID } | undefined
|
|
|
|
|
const plugin = yield* PluginV2.Service
|
|
const plugin = yield* PluginV2.Service
|
|
|
const events = yield* EventV2.Service
|
|
const events = yield* EventV2.Service
|
|
|
|
|
+ const policy = yield* Policy.Service
|
|
|
const scope = yield* Scope.Scope
|
|
const scope = yield* Scope.Scope
|
|
|
|
|
|
|
|
const resolve = (model: ModelV2.Info) => {
|
|
const resolve = (model: ModelV2.Info) => {
|
|
|
- const provider = Option.getOrThrow(HashMap.get(records, model.providerID)).provider
|
|
|
|
|
|
|
+ const provider = state.get().providers.get(model.providerID)!.provider
|
|
|
const endpoint =
|
|
const endpoint =
|
|
|
model.endpoint.type === "unknown"
|
|
model.endpoint.type === "unknown"
|
|
|
? provider.endpoint
|
|
? provider.endpoint
|
|
@@ -120,9 +129,9 @@ export const layer = Layer.effect(
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function* getRecord(providerID: ProviderV2.ID) {
|
|
function* getRecord(providerID: ProviderV2.ID) {
|
|
|
- const match = HashMap.get(records, providerID)
|
|
|
|
|
- if (!match.valueOrUndefined) return yield* new ProviderNotFoundError({ providerID })
|
|
|
|
|
- return match.value
|
|
|
|
|
|
|
+ const match = state.get().providers.get(providerID)
|
|
|
|
|
+ if (!match) return yield* new ProviderNotFoundError({ providerID })
|
|
|
|
|
+ return match
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const normalizeEndpoint = (item: Draft<ProviderV2.Info> | Draft<ModelV2.Info>) => {
|
|
const normalizeEndpoint = (item: Draft<ProviderV2.Info> | Draft<ModelV2.Info>) => {
|
|
@@ -131,114 +140,73 @@ export const layer = Layer.effect(
|
|
|
delete item.options.aisdk.provider.baseURL
|
|
delete item.options.aisdk.provider.baseURL
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const clone = (input: HashMap.HashMap<ProviderV2.ID, ProviderRecord>) =>
|
|
|
|
|
- HashMap.fromIterable(
|
|
|
|
|
- HashMap.toEntries(input).map(([key, value]) => [key, { ...value, models: new Map(value.models) }] as const),
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- const context = (draft: {
|
|
|
|
|
- records: HashMap.HashMap<ProviderV2.ID, ProviderRecord>
|
|
|
|
|
- data: ProviderRecord[]
|
|
|
|
|
- }): Context => {
|
|
|
|
|
- const result: Context = {
|
|
|
|
|
- data: draft.data,
|
|
|
|
|
- updateProvider: (providerID, fn) => result.provider.update(providerID, fn),
|
|
|
|
|
- updateModel: (providerID, modelID, fn) => result.model.update(providerID, modelID, fn),
|
|
|
|
|
- provider: {
|
|
|
|
|
- update: (providerID, fn) => {
|
|
|
|
|
- const current = Option.getOrUndefined(HashMap.get(draft.records, providerID))
|
|
|
|
|
- const provider = produce(current?.provider ?? ProviderV2.Info.empty(providerID), (draft) => {
|
|
|
|
|
- fn(draft)
|
|
|
|
|
- normalizeEndpoint(draft)
|
|
|
|
|
- })
|
|
|
|
|
- const next = {
|
|
|
|
|
- provider,
|
|
|
|
|
- models: current?.models ?? new Map<ModelV2.ID, ModelV2.Info>(),
|
|
|
|
|
- }
|
|
|
|
|
- draft.records = HashMap.set(draft.records, providerID, next)
|
|
|
|
|
- const index = draft.data.findIndex((item) => item.provider.id === providerID)
|
|
|
|
|
- if (index === -1) draft.data.push(next)
|
|
|
|
|
- else draft.data[index] = next
|
|
|
|
|
- },
|
|
|
|
|
- remove: (providerID) => {
|
|
|
|
|
- draft.records = HashMap.remove(draft.records, providerID)
|
|
|
|
|
- const index = draft.data.findIndex((item) => item.provider.id === providerID)
|
|
|
|
|
- if (index !== -1) draft.data.splice(index, 1)
|
|
|
|
|
- },
|
|
|
|
|
- },
|
|
|
|
|
- model: {
|
|
|
|
|
- update: (providerID, modelID, fn) => {
|
|
|
|
|
- const current = Option.getOrThrow(HashMap.get(draft.records, providerID))
|
|
|
|
|
- const model = produce(current.models.get(modelID) ?? ModelV2.Info.empty(providerID, modelID), (draft) => {
|
|
|
|
|
- fn(draft)
|
|
|
|
|
- normalizeEndpoint(draft)
|
|
|
|
|
- })
|
|
|
|
|
- const next = {
|
|
|
|
|
- provider: current.provider,
|
|
|
|
|
- models: new Map(current.models).set(modelID, new ModelV2.Info({ ...model, id: modelID, providerID })),
|
|
|
|
|
- }
|
|
|
|
|
- draft.records = HashMap.set(draft.records, providerID, next)
|
|
|
|
|
- const index = draft.data.findIndex((item) => item.provider.id === providerID)
|
|
|
|
|
- if (index === -1) draft.data.push(next)
|
|
|
|
|
- else draft.data[index] = next
|
|
|
|
|
|
|
+ const state = State.create<Data, Editor>({
|
|
|
|
|
+ initial: () => ({ providers: new Map() }),
|
|
|
|
|
+ editor: (draft) => {
|
|
|
|
|
+ const result: Editor = {
|
|
|
|
|
+ provider: {
|
|
|
|
|
+ list: () => Array.fromIterable(draft.providers.values()) as ProviderRecord[],
|
|
|
|
|
+ get: (providerID) => draft.providers.get(providerID),
|
|
|
|
|
+ update: (providerID, fn) => {
|
|
|
|
|
+ let current = draft.providers.get(providerID)
|
|
|
|
|
+ if (!current) {
|
|
|
|
|
+ current = castDraft({
|
|
|
|
|
+ provider: ProviderV2.Info.empty(providerID),
|
|
|
|
|
+ models: new Map<ModelV2.ID, ModelV2.Info>(),
|
|
|
|
|
+ })
|
|
|
|
|
+ draft.providers.set(providerID, current)
|
|
|
|
|
+ }
|
|
|
|
|
+ fn(current.provider)
|
|
|
|
|
+ normalizeEndpoint(current.provider)
|
|
|
|
|
+ },
|
|
|
|
|
+ remove: (providerID) => {
|
|
|
|
|
+ draft.providers.delete(providerID)
|
|
|
|
|
+ },
|
|
|
},
|
|
},
|
|
|
- remove: (providerID, modelID) => {
|
|
|
|
|
- const current = Option.getOrUndefined(HashMap.get(draft.records, providerID))
|
|
|
|
|
- if (!current) return
|
|
|
|
|
- const next = {
|
|
|
|
|
- provider: current.provider,
|
|
|
|
|
- models: new Map(current.models),
|
|
|
|
|
- }
|
|
|
|
|
- next.models.delete(modelID)
|
|
|
|
|
- draft.records = HashMap.set(draft.records, providerID, next)
|
|
|
|
|
- const index = draft.data.findIndex((item) => item.provider.id === providerID)
|
|
|
|
|
- if (index !== -1) draft.data[index] = next
|
|
|
|
|
|
|
+ model: {
|
|
|
|
|
+ get: (providerID, modelID) => draft.providers.get(providerID)?.models.get(modelID),
|
|
|
|
|
+ update: (providerID, modelID, fn) => {
|
|
|
|
|
+ result.provider.update(providerID, () => {})
|
|
|
|
|
+ const record = draft.providers.get(providerID)!
|
|
|
|
|
+ const model = record.models.get(modelID) ?? castDraft(ModelV2.Info.empty(providerID, modelID))
|
|
|
|
|
+ if (!record.models.has(modelID)) record.models.set(modelID, model)
|
|
|
|
|
+ fn(model)
|
|
|
|
|
+ model.id = modelID
|
|
|
|
|
+ model.providerID = providerID
|
|
|
|
|
+ normalizeEndpoint(model)
|
|
|
|
|
+ },
|
|
|
|
|
+ remove: (providerID, modelID) => {
|
|
|
|
|
+ draft.providers.get(providerID)?.models.delete(modelID)
|
|
|
|
|
+ },
|
|
|
|
|
+ default: {
|
|
|
|
|
+ get: () => draft.defaultModel,
|
|
|
|
|
+ set: (providerID, modelID) => {
|
|
|
|
|
+ draft.defaultModel = { providerID, modelID }
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
},
|
|
},
|
|
|
- },
|
|
|
|
|
- }
|
|
|
|
|
- return result
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const transform = Effect.fn("CatalogV2.transform")(function* () {
|
|
|
|
|
- const draft = { records: clone(records), data: HashMap.toValues(records) }
|
|
|
|
|
- yield* plugin.trigger("catalog.transform", context(draft), {})
|
|
|
|
|
- records = draft.records
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
- const rebuild = Effect.fn("CatalogV2.rebuild")(function* () {
|
|
|
|
|
- const draft = { records: HashMap.empty<ProviderV2.ID, ProviderRecord>(), data: [] as ProviderRecord[] }
|
|
|
|
|
- for (const loader of loaders) loader.update(context(draft))
|
|
|
|
|
- yield* plugin.trigger("catalog.transform", context(draft), {})
|
|
|
|
|
- records = draft.records
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ return result
|
|
|
|
|
+ },
|
|
|
|
|
+ finalize: Effect.fn("CatalogV2.finalize")(function* (catalog, reason) {
|
|
|
|
|
+ if (reason !== "plugin.added") yield* plugin.trigger("catalog.transform", catalog, {}).pipe(Effect.asVoid)
|
|
|
|
|
+ for (const record of [...catalog.provider.list()]) {
|
|
|
|
|
+ if ((yield* policy.evaluate("provider.use", record.provider.id, "allow")) === "deny") {
|
|
|
|
|
+ catalog.provider.remove(record.provider.id)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }),
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- yield* plugin.added().pipe(
|
|
|
|
|
- Stream.runForEach((id) =>
|
|
|
|
|
- Effect.gen(function* () {
|
|
|
|
|
- const draft = { records: clone(records), data: HashMap.toValues(records) }
|
|
|
|
|
- yield* plugin.triggerFor(id, "catalog.transform", context(draft), {})
|
|
|
|
|
- records = draft.records
|
|
|
|
|
- }),
|
|
|
|
|
|
|
+ yield* events.subscribe(PluginV2.Event.Added).pipe(
|
|
|
|
|
+ Stream.runForEach((event) =>
|
|
|
|
|
+ state.update((catalog) => plugin.triggerFor(event.data.id, "catalog.transform", catalog, {}), "plugin.added"),
|
|
|
),
|
|
),
|
|
|
Effect.forkIn(scope, { startImmediately: true }),
|
|
Effect.forkIn(scope, { startImmediately: true }),
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
const result: Interface = {
|
|
const result: Interface = {
|
|
|
- loader: Effect.fn("CatalogV2.loader")(function* () {
|
|
|
|
|
- const loader = { update: (_ctx: Context) => {} }
|
|
|
|
|
- loaders = [...loaders, loader]
|
|
|
|
|
- const scope = yield* Scope.Scope
|
|
|
|
|
- yield* Scope.addFinalizer(
|
|
|
|
|
- scope,
|
|
|
|
|
- Effect.sync(() => {
|
|
|
|
|
- loaders = loaders.filter((item) => item !== loader)
|
|
|
|
|
- }).pipe(Effect.andThen(rebuild())),
|
|
|
|
|
- )
|
|
|
|
|
- return Effect.fnUntraced(function* (update) {
|
|
|
|
|
- loader.update = update
|
|
|
|
|
- yield* rebuild()
|
|
|
|
|
- })
|
|
|
|
|
- }),
|
|
|
|
|
|
|
+ transform: state.transform,
|
|
|
|
|
|
|
|
provider: {
|
|
provider: {
|
|
|
get: Effect.fn("CatalogV2.provider.get")(function* (providerID) {
|
|
get: Effect.fn("CatalogV2.provider.get")(function* (providerID) {
|
|
@@ -247,11 +215,11 @@ export const layer = Layer.effect(
|
|
|
}),
|
|
}),
|
|
|
|
|
|
|
|
all: Effect.fn("CatalogV2.provider.all")(function* () {
|
|
all: Effect.fn("CatalogV2.provider.all")(function* () {
|
|
|
- return globalThis.Array.from(HashMap.values(records)).map((record) => record.provider)
|
|
|
|
|
|
|
+ return Array.fromIterable(state.get().providers.values()).map((record) => record.provider)
|
|
|
}),
|
|
}),
|
|
|
|
|
|
|
|
available: Effect.fn("CatalogV2.provider.available")(function* () {
|
|
available: Effect.fn("CatalogV2.provider.available")(function* () {
|
|
|
- return globalThis.Array.from(HashMap.values(records))
|
|
|
|
|
|
|
+ return Array.fromIterable(state.get().providers.values())
|
|
|
.map((record) => record.provider)
|
|
.map((record) => record.provider)
|
|
|
.filter((provider) => provider.enabled)
|
|
.filter((provider) => provider.enabled)
|
|
|
}),
|
|
}),
|
|
@@ -267,9 +235,8 @@ export const layer = Layer.effect(
|
|
|
|
|
|
|
|
all: Effect.fn("CatalogV2.model.all")(function* () {
|
|
all: Effect.fn("CatalogV2.model.all")(function* () {
|
|
|
return pipe(
|
|
return pipe(
|
|
|
- records,
|
|
|
|
|
- HashMap.toValues,
|
|
|
|
|
- Array.flatMap((record) => globalThis.Array.from(record.models.values())),
|
|
|
|
|
|
|
+ Array.fromIterable(state.get().providers.values()),
|
|
|
|
|
+ Array.flatMap((record) => Array.fromIterable(record.models.values())),
|
|
|
Array.map(resolve),
|
|
Array.map(resolve),
|
|
|
Array.sortWith((item) => item.time.released.epochMilliseconds, Order.flip(Order.Number)),
|
|
Array.sortWith((item) => item.time.released.epochMilliseconds, Order.flip(Order.Number)),
|
|
|
)
|
|
)
|
|
@@ -277,12 +244,13 @@ export const layer = Layer.effect(
|
|
|
|
|
|
|
|
available: Effect.fn("CatalogV2.model.available")(function* () {
|
|
available: Effect.fn("CatalogV2.model.available")(function* () {
|
|
|
return (yield* result.model.all()).filter((model) => {
|
|
return (yield* result.model.all()).filter((model) => {
|
|
|
- const record = Option.getOrUndefined(HashMap.get(records, model.providerID))
|
|
|
|
|
|
|
+ const record = state.get().providers.get(model.providerID)
|
|
|
return record?.provider.enabled !== false && model.enabled
|
|
return record?.provider.enabled !== false && model.enabled
|
|
|
})
|
|
})
|
|
|
}),
|
|
}),
|
|
|
|
|
|
|
|
default: Effect.fn("CatalogV2.model.default")(function* () {
|
|
default: Effect.fn("CatalogV2.model.default")(function* () {
|
|
|
|
|
+ const defaultModel = state.get().defaultModel
|
|
|
if (defaultModel) {
|
|
if (defaultModel) {
|
|
|
const model = yield* result.model.get(defaultModel.providerID, defaultModel.modelID).pipe(Effect.option)
|
|
const model = yield* result.model.get(defaultModel.providerID, defaultModel.modelID).pipe(Effect.option)
|
|
|
if (Option.isSome(model) && model.value.enabled) return model
|
|
if (Option.isSome(model) && model.value.enabled) return model
|
|
@@ -295,13 +263,8 @@ export const layer = Layer.effect(
|
|
|
)
|
|
)
|
|
|
}),
|
|
}),
|
|
|
|
|
|
|
|
- setDefault: Effect.fn("CatalogV2.model.setDefault")(function* (providerID, modelID) {
|
|
|
|
|
- yield* result.model.get(providerID, modelID)
|
|
|
|
|
- defaultModel = { providerID, modelID }
|
|
|
|
|
- }),
|
|
|
|
|
-
|
|
|
|
|
small: Effect.fn("CatalogV2.model.small")(function* (providerID) {
|
|
small: Effect.fn("CatalogV2.model.small")(function* (providerID) {
|
|
|
- const record = Option.getOrUndefined(HashMap.get(records, providerID))
|
|
|
|
|
|
|
+ const record = state.get().providers.get(providerID)
|
|
|
if (!record) return Option.none<ModelV2.Info>()
|
|
if (!record) return Option.none<ModelV2.Info>()
|
|
|
|
|
|
|
|
if (providerID === ProviderV2.ID.opencode) {
|
|
if (providerID === ProviderV2.ID.opencode) {
|
|
@@ -310,7 +273,7 @@ export const layer = Layer.effect(
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const candidates = pipe(
|
|
const candidates = pipe(
|
|
|
- globalThis.Array.from(record.models.values()),
|
|
|
|
|
|
|
+ Array.fromIterable(record.models.values()),
|
|
|
Array.filter(
|
|
Array.filter(
|
|
|
(model) =>
|
|
(model) =>
|
|
|
model.providerID === providerID &&
|
|
model.providerID === providerID &&
|