refactor(llm): brand provider and model identifiers
This commit is contained in:
@@ -5,12 +5,16 @@ import {
|
||||
LLMResponse,
|
||||
Message,
|
||||
ModelCapabilities,
|
||||
ModelID,
|
||||
ModelLimits,
|
||||
ModelRef,
|
||||
ProviderID,
|
||||
ToolChoice,
|
||||
ToolDefinition,
|
||||
type ContentPart,
|
||||
type Protocol,
|
||||
type ModelID as ModelIDType,
|
||||
type ProviderID as ProviderIDType,
|
||||
type ReasoningEffort,
|
||||
type SystemPart,
|
||||
type ToolCallPart,
|
||||
@@ -28,7 +32,9 @@ export type CapabilitiesInput = {
|
||||
}
|
||||
}
|
||||
|
||||
export type ModelInput = Omit<ConstructorParameters<typeof ModelRef>[0], "capabilities" | "limits"> & {
|
||||
export type ModelInput = Omit<ConstructorParameters<typeof ModelRef>[0], "id" | "provider" | "capabilities" | "limits"> & {
|
||||
readonly id: string | ModelIDType
|
||||
readonly provider: string | ProviderIDType
|
||||
readonly capabilities?: ModelCapabilities | CapabilitiesInput
|
||||
readonly limits?: ModelLimits | ConstructorParameters<typeof ModelLimits>[0]
|
||||
}
|
||||
@@ -98,6 +104,8 @@ export const model = (input: ModelInput) => {
|
||||
const { capabilities: modelCapabilities, limits: modelLimits, ...rest } = input
|
||||
return new ModelRef({
|
||||
...rest,
|
||||
id: ModelID.make(input.id),
|
||||
provider: ProviderID.make(input.provider),
|
||||
protocol: input.protocol as Protocol,
|
||||
capabilities: modelCapabilities instanceof ModelCapabilities ? modelCapabilities : capabilities(modelCapabilities),
|
||||
limits: modelLimits instanceof ModelLimits ? modelLimits : limits(modelLimits),
|
||||
|
||||
@@ -1,28 +1,42 @@
|
||||
import type { Protocol } from "./schema"
|
||||
import { ModelID, ProviderID, type Protocol } from "./schema"
|
||||
import type { ModelID as ModelIDType, ProviderID as ProviderIDType } from "./schema"
|
||||
|
||||
export interface ProviderRoute {
|
||||
readonly provider: string
|
||||
readonly provider: ProviderIDType
|
||||
readonly protocol: Protocol
|
||||
}
|
||||
|
||||
export interface ProviderRouteInput {
|
||||
readonly modelID: string
|
||||
readonly providerID: string
|
||||
readonly modelID: ModelIDType
|
||||
readonly providerID: ProviderIDType
|
||||
readonly options: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface ProviderDefinition {
|
||||
readonly id: string
|
||||
readonly id: ProviderIDType
|
||||
readonly route: (input: ProviderRouteInput) => ProviderRoute | undefined
|
||||
}
|
||||
|
||||
export const make = (provider: string, protocol: Protocol): ProviderRoute => ({ provider, protocol })
|
||||
export const make = (provider: string | ProviderIDType, protocol: Protocol): ProviderRoute => ({
|
||||
provider: ProviderID.make(provider),
|
||||
protocol,
|
||||
})
|
||||
|
||||
export const define = (input: ProviderDefinition): ProviderDefinition => input
|
||||
|
||||
export const fixed = (provider: string, protocol: Protocol): ProviderDefinition => {
|
||||
export const fixed = (provider: string | ProviderIDType, protocol: Protocol): ProviderDefinition => {
|
||||
const route = make(provider, protocol)
|
||||
return define({ id: provider, route: () => route })
|
||||
return define({ id: route.provider, route: () => route })
|
||||
}
|
||||
|
||||
export const input = (
|
||||
modelID: string | ModelIDType,
|
||||
providerID: string | ProviderIDType,
|
||||
options: Record<string, unknown>,
|
||||
): ProviderRouteInput => ({
|
||||
modelID: ModelID.make(modelID),
|
||||
providerID: ProviderID.make(providerID),
|
||||
options,
|
||||
})
|
||||
|
||||
export * as ProviderRoute from "./provider-route"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ProviderRoute } from "../provider-route"
|
||||
import { ProviderID } from "../schema"
|
||||
|
||||
export const id = "azure"
|
||||
export const id = ProviderID.make("azure")
|
||||
|
||||
export const provider = ProviderRoute.define({
|
||||
id,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ProviderRoute } from "../provider-route"
|
||||
import { ProviderID } from "../schema"
|
||||
|
||||
export const id = "github-copilot"
|
||||
export const id = ProviderID.make("github-copilot")
|
||||
|
||||
export const shouldUseResponsesApi = (modelID: string) => {
|
||||
const match = /^gpt-(\d+)/.exec(modelID)
|
||||
|
||||
@@ -21,7 +21,7 @@ export const byProvider: Record<string, ProviderFamily> = Object.fromEntries(
|
||||
export const route = (provider: string) => ProviderRoute.make(provider, "openai-compatible-chat")
|
||||
|
||||
export const provider = ProviderRoute.define({
|
||||
id: "openai-compatible",
|
||||
id: ProviderRoute.make("openai-compatible", "openai-compatible-chat").provider,
|
||||
route: (input) => route(input.providerID),
|
||||
})
|
||||
|
||||
|
||||
@@ -10,6 +10,12 @@ export const Protocol = Schema.Literals([
|
||||
])
|
||||
export type Protocol = Schema.Schema.Type<typeof Protocol>
|
||||
|
||||
export const ModelID = Schema.String.pipe(Schema.brand("LLM.ModelID"))
|
||||
export type ModelID = typeof ModelID.Type
|
||||
|
||||
export const ProviderID = Schema.String.pipe(Schema.brand("LLM.ProviderID"))
|
||||
export type ProviderID = typeof ProviderID.Type
|
||||
|
||||
export const ReasoningEfforts = ["none", "minimal", "low", "medium", "high", "xhigh", "max"] as const
|
||||
export const ReasoningEffort = Schema.Literals(ReasoningEfforts)
|
||||
export type ReasoningEffort = Schema.Schema.Type<typeof ReasoningEffort>
|
||||
@@ -61,8 +67,8 @@ export class ModelLimits extends Schema.Class<ModelLimits>("LLM.ModelLimits")({
|
||||
}) {}
|
||||
|
||||
export class ModelRef extends Schema.Class<ModelRef>("LLM.ModelRef")({
|
||||
id: Schema.String,
|
||||
provider: Schema.String,
|
||||
id: ModelID,
|
||||
provider: ProviderID,
|
||||
protocol: Protocol,
|
||||
baseURL: Schema.optional(Schema.String),
|
||||
headers: Schema.optional(Schema.Record(Schema.String, Schema.String)),
|
||||
@@ -351,8 +357,8 @@ export class InvalidRequestError extends Schema.TaggedErrorClass<InvalidRequestE
|
||||
|
||||
export class NoAdapterError extends Schema.TaggedErrorClass<NoAdapterError>()("LLM.NoAdapterError", {
|
||||
protocol: Protocol,
|
||||
provider: Schema.String,
|
||||
model: Schema.String,
|
||||
provider: ProviderID,
|
||||
model: ModelID,
|
||||
}) {
|
||||
override get message() {
|
||||
return `No LLM adapter for ${this.provider}/${this.model} using ${this.protocol}`
|
||||
|
||||
@@ -91,8 +91,8 @@ describe("OpenAI-compatible Chat adapter", () => {
|
||||
providerFamilies.map(([provider, makeModel, baseURL]) => {
|
||||
const model = makeModel({ id: `${provider}-model`, apiKey: "test-key" })
|
||||
return {
|
||||
id: model.id,
|
||||
provider: model.provider,
|
||||
id: String(model.id),
|
||||
provider: String(model.provider),
|
||||
protocol: model.protocol,
|
||||
baseURL: model.baseURL,
|
||||
headers: model.headers,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { Schema } from "effect"
|
||||
import { ContentPart, LLMEvent, LLMRequest, ModelCapabilities, ModelLimits, ModelRef } from "../src/schema"
|
||||
import { ContentPart, LLMEvent, LLMRequest, ModelCapabilities, ModelID, ModelLimits, ModelRef, ProviderID } from "../src/schema"
|
||||
|
||||
const capabilities = new ModelCapabilities({
|
||||
input: { text: true, image: false, audio: false, video: false, pdf: false },
|
||||
@@ -11,8 +11,8 @@ const capabilities = new ModelCapabilities({
|
||||
})
|
||||
|
||||
const model = new ModelRef({
|
||||
id: "fake-model",
|
||||
provider: "fake-provider",
|
||||
id: ModelID.make("fake-model"),
|
||||
provider: ProviderID.make("fake-provider"),
|
||||
protocol: "openai-chat",
|
||||
capabilities,
|
||||
limits: new ModelLimits({}),
|
||||
|
||||
@@ -6,7 +6,8 @@ import { Google } from "@opencode-ai/llm/provider/google"
|
||||
import { OpenAI } from "@opencode-ai/llm/provider/openai"
|
||||
import { OpenAICompatibleFamily } from "@opencode-ai/llm/provider/openai-compatible-family"
|
||||
import { XAI } from "@opencode-ai/llm/provider/xai"
|
||||
import type { ProviderDefinition, ProviderRoute } from "@opencode-ai/llm/provider-route"
|
||||
import { ProviderRoute } from "@opencode-ai/llm/provider-route"
|
||||
import type { ProviderDefinition, ProviderRoute as ProviderRouteType } from "@opencode-ai/llm/provider-route"
|
||||
import { ReasoningEfforts, type ModelRef, type Protocol, type ReasoningEffort } from "@opencode-ai/llm/schema"
|
||||
import { isRecord } from "@/util/record"
|
||||
import type * as Provider from "./provider"
|
||||
@@ -48,12 +49,8 @@ const recordOption = (options: Record<string, unknown>, key: string): Record<str
|
||||
export const route = (
|
||||
input: Input,
|
||||
options: Record<string, unknown> = { ...input.provider.options, ...input.model.options },
|
||||
): ProviderRoute | undefined =>
|
||||
PROVIDERS[input.model.api.npm]?.route({
|
||||
modelID: input.model.api.id,
|
||||
providerID: input.model.providerID,
|
||||
options,
|
||||
})
|
||||
): ProviderRouteType | undefined =>
|
||||
PROVIDERS[input.model.api.npm]?.route(ProviderRoute.input(input.model.api.id, input.model.providerID, options))
|
||||
|
||||
const baseURL = (input: Input, selected: Protocol, options: Record<string, unknown>) => {
|
||||
const configured = stringOption(options, "baseURL") ?? input.model.api.url
|
||||
|
||||
Reference in New Issue
Block a user