opencode/packages/console/core/src/model.ts

183 lines
5.5 KiB
TypeScript
Raw Normal View History

2025-09-27 03:18:22 +08:00
import { z } from "zod"
2025-10-08 12:03:34 +08:00
import { eq, and } from "drizzle-orm"
import { Database } from "./drizzle"
import { ModelTable } from "./schema/model.sql"
import { Identifier } from "./identifier"
import { fn } from "./util/fn"
import { Actor } from "./actor"
import { Resource } from "@opencode-ai/console-resource"
2025-09-27 03:18:22 +08:00
2025-10-17 03:58:49 +08:00
export namespace ZenData {
2025-11-19 03:28:27 +08:00
const FormatSchema = z.enum(["anthropic", "google", "openai", "oa-compat"])
2025-12-12 12:41:04 +08:00
const TrialSchema = z.object({
provider: z.string(),
limits: z.array(
z.object({
limit: z.number(),
client: z.enum(["cli", "desktop"]).optional(),
}),
),
})
2026-02-02 13:29:52 +08:00
const RateLimitSchema = z.object({
period: z.enum(["day", "rolling"]),
value: z.number().int(),
2026-02-03 13:17:03 +08:00
checkHeader: z.string().optional(),
fallbackValue: z.number().int().optional(),
2026-02-02 13:29:52 +08:00
})
2025-11-04 06:30:16 +08:00
export type Format = z.infer<typeof FormatSchema>
2025-12-12 12:41:04 +08:00
export type Trial = z.infer<typeof TrialSchema>
2026-02-02 13:29:52 +08:00
export type RateLimit = z.infer<typeof RateLimitSchema>
2025-11-04 06:30:16 +08:00
2025-09-27 03:18:22 +08:00
const ModelCostSchema = z.object({
input: z.number(),
output: z.number(),
cacheRead: z.number().optional(),
cacheWrite5m: z.number().optional(),
cacheWrite1h: z.number().optional(),
})
2025-10-17 03:58:49 +08:00
const ModelSchema = z.object({
2025-10-12 03:07:06 +08:00
name: z.string(),
2025-09-27 03:18:22 +08:00
cost: ModelCostSchema,
cost200K: ModelCostSchema.optional(),
allowAnonymous: z.boolean().optional(),
2025-12-05 10:53:31 +08:00
byokProvider: z.enum(["openai", "anthropic", "google"]).optional(),
2026-01-16 14:07:00 +08:00
stickyProvider: z.enum(["strict", "prefer"]).optional(),
2025-12-12 12:41:04 +08:00
trial: TrialSchema.optional(),
2026-02-02 13:29:52 +08:00
rateLimit: RateLimitSchema.optional(),
2025-11-11 13:29:42 +08:00
fallbackProvider: z.string().optional(),
2025-09-27 03:18:22 +08:00
providers: z.array(
z.object({
id: z.string(),
model: z.string(),
weight: z.number().optional(),
disabled: z.boolean().optional(),
2025-11-24 04:21:47 +08:00
storeModel: z.string().optional(),
2025-09-27 03:18:22 +08:00
}),
),
})
2025-10-17 03:58:49 +08:00
const ProviderSchema = z.object({
api: z.string(),
apiKey: z.string(),
2026-02-11 13:05:04 +08:00
format: FormatSchema.optional(),
2025-10-17 03:58:49 +08:00
headerMappings: z.record(z.string(), z.string()).optional(),
2026-02-14 13:47:24 +08:00
payloadModifier: z.record(z.string(), z.any()).optional(),
2026-02-11 13:05:04 +08:00
family: z.string().optional(),
})
const ProviderFamilySchema = z.object({
headers: z.record(z.string(), z.string()).optional(),
2026-02-14 13:47:24 +08:00
responseModifier: z.record(z.string(), z.string()).optional(),
2025-10-17 03:58:49 +08:00
})
const ModelsSchema = z.object({
2025-12-12 12:41:04 +08:00
models: z.record(z.string(), z.union([ModelSchema, z.array(ModelSchema.extend({ formatFilter: FormatSchema }))])),
2025-10-17 03:58:49 +08:00
providers: z.record(z.string(), ProviderSchema),
2026-02-11 13:05:04 +08:00
providerFamilies: z.record(z.string(), ProviderFamilySchema),
2025-10-17 03:58:49 +08:00
})
2025-10-08 12:03:34 +08:00
2025-10-17 03:58:49 +08:00
export const validate = fn(ModelsSchema, (input) => {
return input
})
export const list = fn(z.void(), () => {
2025-11-22 01:50:51 +08:00
const json = JSON.parse(
2025-12-12 12:41:04 +08:00
Resource.ZEN_MODELS1.value +
Resource.ZEN_MODELS2.value +
Resource.ZEN_MODELS3.value +
Resource.ZEN_MODELS4.value +
2025-12-24 09:36:52 +08:00
Resource.ZEN_MODELS5.value +
2026-01-04 13:58:06 +08:00
Resource.ZEN_MODELS6.value +
2026-01-16 07:21:01 +08:00
Resource.ZEN_MODELS7.value +
2026-01-31 01:19:36 +08:00
Resource.ZEN_MODELS8.value +
Resource.ZEN_MODELS9.value +
2026-02-08 23:31:07 +08:00
Resource.ZEN_MODELS10.value +
Resource.ZEN_MODELS11.value +
Resource.ZEN_MODELS12.value +
Resource.ZEN_MODELS13.value +
Resource.ZEN_MODELS14.value +
Resource.ZEN_MODELS15.value +
Resource.ZEN_MODELS16.value +
Resource.ZEN_MODELS17.value +
Resource.ZEN_MODELS18.value +
Resource.ZEN_MODELS19.value +
2026-02-17 14:32:57 +08:00
Resource.ZEN_MODELS20.value +
Resource.ZEN_MODELS21.value +
Resource.ZEN_MODELS22.value +
Resource.ZEN_MODELS23.value +
Resource.ZEN_MODELS24.value +
Resource.ZEN_MODELS25.value +
Resource.ZEN_MODELS26.value +
Resource.ZEN_MODELS27.value +
Resource.ZEN_MODELS28.value +
Resource.ZEN_MODELS29.value +
Resource.ZEN_MODELS30.value,
2025-11-22 01:50:51 +08:00
)
2026-02-11 13:05:04 +08:00
const { models, providers, providerFamilies } = ModelsSchema.parse(json)
return {
models,
providers: Object.fromEntries(
Object.entries(providers).map(([id, provider]) => [
id,
{ ...provider, ...(provider.family ? providerFamilies[provider.family] : {}) },
]),
),
}
2025-10-17 03:58:49 +08:00
})
2025-10-08 12:03:34 +08:00
}
export namespace Model {
export const enable = fn(z.object({ model: z.string() }), ({ model }) => {
2025-10-10 12:02:04 +08:00
Actor.assertAdmin()
2025-10-08 12:03:34 +08:00
return Database.use((db) =>
2025-11-08 09:59:02 +08:00
db.delete(ModelTable).where(and(eq(ModelTable.workspaceID, Actor.workspace()), eq(ModelTable.model, model))),
2025-10-08 12:03:34 +08:00
)
})
export const disable = fn(z.object({ model: z.string() }), ({ model }) => {
2025-10-10 12:02:04 +08:00
Actor.assertAdmin()
2025-10-08 12:03:34 +08:00
return Database.use((db) =>
db
.insert(ModelTable)
.values({
id: Identifier.create("model"),
workspaceID: Actor.workspace(),
model: model,
})
.onDuplicateKeyUpdate({
set: {
timeDeleted: null,
},
}),
)
})
export const listDisabled = fn(z.void(), () => {
return Database.use((db) =>
db
.select({ model: ModelTable.model })
.from(ModelTable)
.where(eq(ModelTable.workspaceID, Actor.workspace()))
.then((rows) => rows.map((row) => row.model)),
)
})
export const isDisabled = fn(
z.object({
model: z.string(),
}),
({ model }) => {
return Database.use(async (db) => {
const result = await db
.select()
.from(ModelTable)
.where(and(eq(ModelTable.workspaceID, Actor.workspace()), eq(ModelTable.model, model)))
.limit(1)
return result.length > 0
})
},
)
2025-09-27 03:18:22 +08:00
}