refactor: split up models.dev and config model definitions to prevent coupling (#20605)

pull/20702/head
Aiden Cline 2026-04-02 10:00:43 -05:00 committed by GitHub
parent ec3ae17e4d
commit 23c8656080
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 70 additions and 26 deletions

View File

@ -730,28 +730,77 @@ export namespace Config {
}) })
export type Layout = z.infer<typeof Layout> export type Layout = z.infer<typeof Layout>
export const Model = z
.object({
id: z.string(),
name: z.string(),
family: z.string().optional(),
release_date: z.string(),
attachment: z.boolean(),
reasoning: z.boolean(),
temperature: z.boolean(),
tool_call: z.boolean(),
interleaved: z
.union([
z.literal(true),
z
.object({
field: z.enum(["reasoning_content", "reasoning_details"]),
})
.strict(),
])
.optional(),
cost: z
.object({
input: z.number(),
output: z.number(),
cache_read: z.number().optional(),
cache_write: z.number().optional(),
context_over_200k: z
.object({
input: z.number(),
output: z.number(),
cache_read: z.number().optional(),
cache_write: z.number().optional(),
})
.optional(),
})
.optional(),
limit: z.object({
context: z.number(),
input: z.number().optional(),
output: z.number(),
}),
modalities: z
.object({
input: z.array(z.enum(["text", "audio", "image", "video", "pdf"])),
output: z.array(z.enum(["text", "audio", "image", "video", "pdf"])),
})
.optional(),
experimental: z.boolean().optional(),
status: z.enum(["alpha", "beta", "deprecated"]).optional(),
options: z.record(z.string(), z.any()),
headers: z.record(z.string(), z.string()).optional(),
provider: z.object({ npm: z.string().optional(), api: z.string().optional() }).optional(),
variants: z
.record(
z.string(),
z
.object({
disabled: z.boolean().optional().describe("Disable this variant for the model"),
})
.catchall(z.any()),
)
.optional()
.describe("Variant-specific configuration"),
})
.partial()
export const Provider = ModelsDev.Provider.partial() export const Provider = ModelsDev.Provider.partial()
.extend({ .extend({
whitelist: z.array(z.string()).optional(), whitelist: z.array(z.string()).optional(),
blacklist: z.array(z.string()).optional(), blacklist: z.array(z.string()).optional(),
models: z models: z.record(z.string(), Model).optional(),
.record(
z.string(),
ModelsDev.Model.partial().extend({
variants: z
.record(
z.string(),
z
.object({
disabled: z.boolean().optional().describe("Disable this variant for the model"),
})
.catchall(z.any()),
)
.optional()
.describe("Variant-specific configuration"),
}),
)
.optional(),
options: z options: z
.object({ .object({
apiKey: z.string().optional(), apiKey: z.string().optional(),

View File

@ -61,12 +61,8 @@ export namespace ModelsDev {
output: z.array(z.enum(["text", "audio", "image", "video", "pdf"])), output: z.array(z.enum(["text", "audio", "image", "video", "pdf"])),
}) })
.optional(), .optional(),
experimental: z.boolean().optional(),
status: z.enum(["alpha", "beta", "deprecated"]).optional(), status: z.enum(["alpha", "beta", "deprecated"]).optional(),
options: z.record(z.string(), z.any()),
headers: z.record(z.string(), z.string()).optional(),
provider: z.object({ npm: z.string().optional(), api: z.string().optional() }).optional(), provider: z.object({ npm: z.string().optional(), api: z.string().optional() }).optional(),
variants: z.record(z.string(), z.record(z.string(), z.any())).optional(),
}) })
export type Model = z.infer<typeof Model> export type Model = z.infer<typeof Model>

View File

@ -903,8 +903,8 @@ export namespace Provider {
npm: model.provider?.npm ?? provider.npm ?? "@ai-sdk/openai-compatible", npm: model.provider?.npm ?? provider.npm ?? "@ai-sdk/openai-compatible",
}, },
status: model.status ?? "active", status: model.status ?? "active",
headers: model.headers ?? {}, headers: {},
options: model.options ?? {}, options: {},
cost: { cost: {
input: model.cost?.input ?? 0, input: model.cost?.input ?? 0,
output: model.cost?.output ?? 0, output: model.cost?.output ?? 0,

View File

@ -1,7 +1,6 @@
import { Provider } from "@/provider/provider" import { Provider } from "@/provider/provider"
import { Log } from "@/util/log" import { Log } from "@/util/log"
import { Cause, Effect, Layer, Record, ServiceMap } from "effect" import { Effect, Layer, Record, ServiceMap } from "effect"
import * as Queue from "effect/Queue"
import * as Stream from "effect/Stream" import * as Stream from "effect/Stream"
import { streamText, wrapLanguageModel, type ModelMessage, type Tool, tool, jsonSchema } from "ai" import { streamText, wrapLanguageModel, type ModelMessage, type Tool, tool, jsonSchema } from "ai"
import { mergeDeep, pipe } from "remeda" import { mergeDeep, pipe } from "remeda"