feat(llm): add model defaults and compatibility data (#34436)

This commit is contained in:
Shoubhit Dash 2026-06-29 19:10:00 +05:30 committed by GitHub
parent 6d9539f469
commit 1fd8bf526d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 89 additions and 5 deletions

View file

@ -1,7 +1,6 @@
import type { RouteDefaultsInput } from "./route/client"
import type { Model, ModelID, ProviderID } from "./schema"
export type ModelOptions = RouteDefaultsInput
export type ModelOptions = Pick<Model.Input, "defaults" | "compatibility">
/**
* Advanced structural provider definition helper. Built-in providers should

View file

@ -134,15 +134,60 @@ export namespace ModelLimits {
input instanceof ModelLimits ? input : new ModelLimits(input ?? {})
}
export class ModelDefaults extends Schema.Class<ModelDefaults>("LLM.ModelDefaults")({
limits: Schema.optional(ModelLimits),
generation: Schema.optional(GenerationOptions),
providerOptions: Schema.optional(ProviderOptions),
http: Schema.optional(HttpOptions),
}) {}
export namespace ModelDefaults {
export type Input = ModelDefaults | {
readonly limits?: ModelLimits.Input
readonly generation?: GenerationOptions.Input
readonly providerOptions?: ProviderOptions
readonly http?: HttpOptions.Input
}
/** Normalize selected-model request defaults without applying precedence. */
export const make = (input: Input) => {
if (input instanceof ModelDefaults) return input
return new ModelDefaults({
limits: input.limits === undefined ? undefined : ModelLimits.make(input.limits),
generation: input.generation === undefined ? undefined : GenerationOptions.make(input.generation),
providerOptions: input.providerOptions,
http: input.http === undefined ? undefined : HttpOptions.make(input.http),
})
}
}
export const ModelToolSchemaCompatibility = Schema.Literals(["gemini", "moonshot"])
export type ModelToolSchemaCompatibility = Schema.Schema.Type<typeof ModelToolSchemaCompatibility>
export class ModelCompatibility extends Schema.Class<ModelCompatibility>("LLM.ModelCompatibility")({
toolSchema: Schema.optional(ModelToolSchemaCompatibility),
}) {}
export namespace ModelCompatibility {
export type Input = ModelCompatibility | ConstructorParameters<typeof ModelCompatibility>[0]
/** Normalize model/upstream compatibility metadata without projecting requests. */
export const make = (input: Input) => (input instanceof ModelCompatibility ? input : new ModelCompatibility(input))
}
export class Model {
readonly id: ModelID
readonly provider: ProviderID
readonly route: AnyRoute
readonly defaults?: ModelDefaults
readonly compatibility?: ModelCompatibility
constructor(input: Model.ConstructorInput) {
this.id = input.id
this.provider = input.provider
this.route = input.route
this.defaults = input.defaults
this.compatibility = input.compatibility
}
static make(input: Model.Input) {
@ -150,6 +195,8 @@ export class Model {
id: ModelID.make(input.id),
provider: ProviderID.make(input.provider),
route: input.route,
defaults: input.defaults === undefined ? undefined : ModelDefaults.make(input.defaults),
compatibility: input.compatibility === undefined ? undefined : ModelCompatibility.make(input.compatibility),
})
}
@ -158,6 +205,8 @@ export class Model {
id: model.id,
provider: model.provider,
route: model.route,
defaults: model.defaults,
compatibility: model.compatibility,
}
}
@ -175,11 +224,15 @@ export namespace Model {
readonly id: ModelID
readonly provider: ProviderID
readonly route: AnyRoute
readonly defaults?: ModelDefaults
readonly compatibility?: ModelCompatibility
}
export type Input = Omit<ConstructorInput, "id" | "provider"> & {
export type Input = Omit<ConstructorInput, "id" | "provider" | "defaults" | "compatibility"> & {
readonly id: string | ModelID
readonly provider: string | ProviderID
readonly defaults?: ModelDefaults.Input
readonly compatibility?: ModelCompatibility.Input
}
}

View file

@ -90,15 +90,47 @@ describe("llm constructors", () => {
provider: "fake",
route: chatRoute,
})
const updated = Model.update(base, { route: responsesRoute })
const updated = Model.update(base, {
route: responsesRoute,
defaults: { generation: { maxTokens: 20 } },
compatibility: { toolSchema: "gemini" },
})
const updatedInput = Model.input(updated)
expect(updated).toBeInstanceOf(Model)
expect(String(updated.id)).toBe("fake-model")
expect(updated.route).toBe(responsesRoute)
expect(String(Model.input(updated).provider)).toBe("fake")
expect(updated.defaults?.generation).toEqual({ maxTokens: 20 })
expect(updated.compatibility).toEqual({ toolSchema: "gemini" })
expect(updatedInput.defaults).toBe(updated.defaults)
expect(updatedInput.compatibility).toBe(updated.compatibility)
expect(String(updatedInput.provider)).toBe("fake")
expect(Model.update(updated, {})).toBe(updated)
})
test("carries model defaults and compatibility through route model selection", () => {
const model = chatRoute.model({
id: "kimi-k2",
defaults: {
limits: { context: 128_000, output: 8_192 },
generation: { maxTokens: 1_024, stop: ["END"] },
providerOptions: { openai: { parallelToolCalls: false } },
http: { body: { extra_body: true } },
},
compatibility: { toolSchema: "moonshot" },
})
const request = LLM.request({ model, prompt: "Say hello." })
expect(request.model.defaults?.limits).toEqual({ context: 128_000, output: 8_192 })
expect(request.model.defaults?.generation).toEqual({ maxTokens: 1_024, stop: ["END"] })
expect(request.model.defaults?.providerOptions).toEqual({ openai: { parallelToolCalls: false } })
expect(request.model.defaults?.http).toEqual({ body: { extra_body: true } })
expect(request.model.compatibility).toEqual({ toolSchema: "moonshot" })
expect(request.generation).toBeUndefined()
expect(request.providerOptions).toBeUndefined()
expect(request.http).toBeUndefined()
})
test("builds tool choices from names and tools", () => {
const tool = ToolDefinition.make({ name: "lookup", description: "Lookup data", inputSchema: { type: "object" } })