refactor(llm): collapse model construction to provider packages and routes
Provider packages are now the only module-level model constructors; the
protocol-level model wrappers added earlier on this branch had one call
site each and duplicated the Settings vocabulary, so packages call
route.with(...).model({id}) directly. Deletes provider facades with no
importers outside llm tests; configure stays for V1 native-request.
This commit is contained in:
parent
759db0d172
commit
5e5294c036
12 changed files with 117 additions and 221 deletions
|
|
@ -1,13 +1,11 @@
|
|||
import { Effect, Schema } from "effect"
|
||||
import { Route, type RoutePatch } from "../route/client"
|
||||
import { Auth, type Auth as AuthDef } from "../route/auth"
|
||||
import { Route } from "../route/client"
|
||||
import { Auth } from "../route/auth"
|
||||
import { Endpoint } from "../route/endpoint"
|
||||
import { Framing } from "../route/framing"
|
||||
import { Protocol } from "../route/protocol"
|
||||
import {
|
||||
LLMEvent,
|
||||
type Model,
|
||||
type ProviderOptions,
|
||||
Usage,
|
||||
type CacheHint,
|
||||
type FinishReason,
|
||||
|
|
@ -854,25 +852,4 @@ export const route = Route.make({
|
|||
headers: () => ({ "anthropic-version": "2023-06-01" }),
|
||||
})
|
||||
|
||||
export interface ModelConfig {
|
||||
readonly auth: AuthDef
|
||||
readonly baseURL?: string
|
||||
readonly headers?: Readonly<Record<string, string>>
|
||||
readonly providerOptions?: ProviderOptions
|
||||
readonly body?: Readonly<Record<string, unknown>>
|
||||
readonly limits?: { readonly context: number; readonly output: number }
|
||||
}
|
||||
|
||||
export const model = (id: string, config: ModelConfig): Model =>
|
||||
route
|
||||
.with({
|
||||
auth: config.auth,
|
||||
endpoint: config.baseURL === undefined ? undefined : { baseURL: config.baseURL },
|
||||
headers: config.headers,
|
||||
providerOptions: config.providerOptions,
|
||||
http: config.body === undefined ? undefined : { body: config.body },
|
||||
limits: config.limits,
|
||||
} satisfies RoutePatch<AnthropicMessagesBody, unknown>)
|
||||
.model({ id })
|
||||
|
||||
export * as AnthropicMessages from "./anthropic-messages"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import { Route, type RoutePatch, type RouteRoutedModelInput } from "../route/client"
|
||||
import type { Auth as AuthDef } from "../route/auth"
|
||||
import { Route, type RouteRoutedModelInput } from "../route/client"
|
||||
import { Endpoint } from "../route/endpoint"
|
||||
import { Framing } from "../route/framing"
|
||||
import type { Model, ProviderOptions } from "../schema"
|
||||
import * as OpenAIChat from "./openai-chat"
|
||||
|
||||
const ADAPTER = "openai-compatible-chat"
|
||||
|
|
@ -23,25 +21,4 @@ export const route = Route.make({
|
|||
framing: Framing.sse,
|
||||
})
|
||||
|
||||
export interface ModelConfig {
|
||||
readonly auth: AuthDef
|
||||
readonly baseURL?: string
|
||||
readonly headers?: Readonly<Record<string, string>>
|
||||
readonly providerOptions?: ProviderOptions
|
||||
readonly body?: Readonly<Record<string, unknown>>
|
||||
readonly limits?: { readonly context: number; readonly output: number }
|
||||
}
|
||||
|
||||
export const model = (id: string, config: ModelConfig): Model =>
|
||||
route
|
||||
.with({
|
||||
auth: config.auth,
|
||||
endpoint: config.baseURL === undefined ? undefined : { baseURL: config.baseURL },
|
||||
headers: config.headers,
|
||||
providerOptions: config.providerOptions,
|
||||
http: config.body === undefined ? undefined : { body: config.body },
|
||||
limits: config.limits,
|
||||
} satisfies RoutePatch<OpenAIChat.OpenAIChatBody, unknown>)
|
||||
.model({ id, provider: "openai-compatible" })
|
||||
|
||||
export * as OpenAICompatibleChat from "./openai-compatible-chat"
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
import { Effect, Schema } from "effect"
|
||||
import { Route, type RoutePatch } from "../route/client"
|
||||
import { Auth, type Auth as AuthDef } from "../route/auth"
|
||||
import { Route } from "../route/client"
|
||||
import { Auth } from "../route/auth"
|
||||
import { Endpoint } from "../route/endpoint"
|
||||
import { HttpTransport, WebSocketTransport } from "../route/transport"
|
||||
import { Protocol } from "../route/protocol"
|
||||
import {
|
||||
LLMEvent,
|
||||
type Model,
|
||||
type ProviderOptions,
|
||||
Usage,
|
||||
type FinishReason,
|
||||
type JsonSchema,
|
||||
|
|
@ -993,27 +991,6 @@ export const route = Route.make({
|
|||
defaults: { providerOptions: { openai: { store: false } } },
|
||||
})
|
||||
|
||||
export interface ModelConfig {
|
||||
readonly auth: AuthDef
|
||||
readonly baseURL?: string
|
||||
readonly headers?: Readonly<Record<string, string>>
|
||||
readonly providerOptions?: ProviderOptions
|
||||
readonly body?: Readonly<Record<string, unknown>>
|
||||
readonly limits?: { readonly context: number; readonly output: number }
|
||||
}
|
||||
|
||||
export const model = (id: string, config: ModelConfig): Model =>
|
||||
route
|
||||
.with({
|
||||
auth: config.auth,
|
||||
endpoint: config.baseURL === undefined ? undefined : { baseURL: config.baseURL },
|
||||
headers: config.headers,
|
||||
providerOptions: config.providerOptions,
|
||||
http: config.body === undefined ? undefined : { body: config.body },
|
||||
limits: config.limits,
|
||||
} satisfies RoutePatch<OpenAIResponsesBody, unknown>)
|
||||
.model({ id })
|
||||
|
||||
const decodeWebSocketMessage = ProviderShared.validateWith(Schema.decodeUnknownEffect(OpenAIResponsesWebSocketMessage))
|
||||
|
||||
const webSocketMessage = (body: OpenAIResponsesBody | Record<string, unknown>) =>
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ import { Auth } from "../route/auth"
|
|||
import type { ProviderAuthOption } from "../route/auth-options"
|
||||
import { ProviderID, type ModelID } from "../schema"
|
||||
import { ProviderPackage } from "../provider-package"
|
||||
import * as AnthropicMessages from "../protocols/anthropic-messages"
|
||||
import { AnthropicMessages } from "../protocols/anthropic-messages"
|
||||
|
||||
export const id = ProviderID.make("anthropic")
|
||||
|
||||
export const routes = [AnthropicMessages.route]
|
||||
|
||||
export type Config = RouteDefaultsInput & ProviderAuthOption<"optional"> & { readonly baseURL?: string }
|
||||
type Config = RouteDefaultsInput & ProviderAuthOption<"optional"> & { readonly baseURL?: string }
|
||||
|
||||
export interface AnthropicSettings extends ProviderPackage.Settings {}
|
||||
|
||||
|
|
@ -34,14 +34,15 @@ export const configure = (input: Config = {}) => {
|
|||
}
|
||||
}
|
||||
|
||||
export const provider = configure()
|
||||
export const model = ProviderPackage.define((modelID, settings: AnthropicSettings) =>
|
||||
AnthropicMessages.model(modelID, {
|
||||
auth: settings.apiKey === undefined ? Auth.none : Auth.header("x-api-key", settings.apiKey),
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers,
|
||||
providerOptions: settings.providerOptions === undefined ? undefined : { anthropic: settings.providerOptions },
|
||||
body: settings.body,
|
||||
limits: settings.limits,
|
||||
}),
|
||||
AnthropicMessages.route
|
||||
.with({
|
||||
auth: settings.apiKey === undefined ? Auth.none : Auth.header("x-api-key", settings.apiKey),
|
||||
endpoint: { baseURL: settings.baseURL },
|
||||
headers: settings.headers,
|
||||
providerOptions: settings.providerOptions && { anthropic: settings.providerOptions },
|
||||
http: { body: settings.body },
|
||||
limits: settings.limits,
|
||||
})
|
||||
.model({ id: modelID }),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import { ProviderID, type ModelID } from "../schema"
|
||||
import * as OpenAICompatibleChat from "../protocols/openai-compatible-chat"
|
||||
import { OpenAICompatibleChat } from "../protocols/openai-compatible-chat"
|
||||
import type { RouteDefaultsInput } from "../route/client"
|
||||
import { AuthOptions, type ProviderAuthOption } from "../route/auth-options"
|
||||
import { Auth } from "../route/auth"
|
||||
import { ProviderPackage } from "../provider-package"
|
||||
import { profiles, type OpenAICompatibleProfile } from "./openai-compatible-profile"
|
||||
|
||||
export const id = ProviderID.make("openai-compatible")
|
||||
|
||||
|
|
@ -14,11 +13,6 @@ type GenericModelOptions = RouteDefaultsInput &
|
|||
readonly baseURL: string
|
||||
}
|
||||
|
||||
export type FamilyModelOptions = RouteDefaultsInput &
|
||||
ProviderAuthOption<"optional"> & {
|
||||
readonly baseURL?: string
|
||||
}
|
||||
|
||||
export interface OpenAICompatibleSettings extends ProviderPackage.Settings {}
|
||||
|
||||
export const routes = [OpenAICompatibleChat.route]
|
||||
|
|
@ -39,42 +33,15 @@ export const configure = (input: GenericModelOptions) => {
|
|||
}
|
||||
}
|
||||
|
||||
const define = (profile: OpenAICompatibleProfile) => {
|
||||
const configureProfile = (input: FamilyModelOptions = {}) => {
|
||||
const facade = configure({
|
||||
...input,
|
||||
baseURL: input.baseURL ?? profile.baseURL,
|
||||
provider: profile.provider,
|
||||
})
|
||||
return {
|
||||
id: ProviderID.make(profile.provider),
|
||||
model: facade.model,
|
||||
configure: configureProfile,
|
||||
}
|
||||
}
|
||||
return configureProfile()
|
||||
}
|
||||
|
||||
export const provider = {
|
||||
id,
|
||||
configure,
|
||||
}
|
||||
|
||||
export const model = ProviderPackage.define((modelID, settings: OpenAICompatibleSettings) =>
|
||||
OpenAICompatibleChat.model(modelID, {
|
||||
auth: settings.apiKey === undefined ? Auth.none : Auth.bearer(settings.apiKey),
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers,
|
||||
providerOptions: settings.providerOptions === undefined ? undefined : { openai: settings.providerOptions },
|
||||
body: settings.body,
|
||||
limits: settings.limits,
|
||||
}),
|
||||
OpenAICompatibleChat.route
|
||||
.with({
|
||||
auth: settings.apiKey === undefined ? Auth.none : Auth.bearer(settings.apiKey),
|
||||
endpoint: { baseURL: settings.baseURL },
|
||||
headers: settings.headers,
|
||||
providerOptions: settings.providerOptions && { openai: settings.providerOptions },
|
||||
http: { body: settings.body },
|
||||
limits: settings.limits,
|
||||
})
|
||||
.model({ id: modelID, provider: "openai-compatible" }),
|
||||
)
|
||||
|
||||
export const baseten = define(profiles.baseten)
|
||||
export const cerebras = define(profiles.cerebras)
|
||||
export const deepinfra = define(profiles.deepinfra)
|
||||
export const deepseek = define(profiles.deepseek)
|
||||
export const fireworks = define(profiles.fireworks)
|
||||
export const groq = define(profiles.groq)
|
||||
export const togetherai = define(profiles.togetherai)
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import { Auth } from "../route/auth"
|
|||
import type { Route, RouteDefaultsInput } from "../route/client"
|
||||
import { ProviderID, type ModelID } from "../schema"
|
||||
import { ProviderPackage } from "../provider-package"
|
||||
import * as OpenAIChat from "../protocols/openai-chat"
|
||||
import * as OpenAIResponses from "../protocols/openai-responses"
|
||||
import { OpenAIChat } from "../protocols/openai-chat"
|
||||
import { OpenAIResponses } from "../protocols/openai-responses"
|
||||
import { withOpenAIOptions, type OpenAIProviderOptionsInput } from "./openai-options"
|
||||
|
||||
export type { OpenAIOptionsInput, OpenAIResponseIncludable } from "./openai-options"
|
||||
|
|
@ -16,7 +16,7 @@ export const routes = [OpenAIResponses.route, OpenAIResponses.webSocketRoute, Op
|
|||
// This provider facade wraps the lower-level Responses and Chat model factories
|
||||
// with OpenAI-specific conveniences: typed options, API-key sugar, env fallback,
|
||||
// and default option normalization.
|
||||
export type Config = RouteDefaultsInput &
|
||||
type Config = RouteDefaultsInput &
|
||||
ProviderAuthOption<"optional"> & {
|
||||
readonly baseURL?: string
|
||||
readonly queryParams?: Record<string, string>
|
||||
|
|
@ -59,18 +59,15 @@ export const configure = (input: Config = {}) => {
|
|||
}
|
||||
}
|
||||
|
||||
export const provider = configure()
|
||||
|
||||
export const model = ProviderPackage.define((modelID, settings: OpenAISettings) =>
|
||||
OpenAIResponses.model(modelID, {
|
||||
auth: settings.apiKey === undefined ? Auth.none : Auth.bearer(settings.apiKey),
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers,
|
||||
providerOptions: settings.providerOptions === undefined ? undefined : { openai: settings.providerOptions },
|
||||
body: settings.body,
|
||||
limits: settings.limits,
|
||||
}),
|
||||
OpenAIResponses.route
|
||||
.with({
|
||||
auth: settings.apiKey === undefined ? Auth.none : Auth.bearer(settings.apiKey),
|
||||
endpoint: { baseURL: settings.baseURL },
|
||||
headers: settings.headers,
|
||||
providerOptions: settings.providerOptions && { openai: settings.providerOptions },
|
||||
http: { body: settings.body },
|
||||
limits: settings.limits,
|
||||
})
|
||||
.model({ id: modelID }),
|
||||
)
|
||||
export const responses = provider.responses
|
||||
export const responsesWebSocket = provider.responsesWebSocket
|
||||
export const chat = provider.chat
|
||||
|
|
|
|||
|
|
@ -7,14 +7,16 @@ export interface OpenAICodexSettings extends ProviderPackage.Settings {
|
|||
}
|
||||
|
||||
export const model = ProviderPackage.define((modelID, settings: OpenAICodexSettings) =>
|
||||
OpenAIResponses.model(modelID, {
|
||||
auth: (settings.apiKey === undefined ? Auth.none : Auth.bearer(settings.apiKey)).andThen(
|
||||
settings.accountID === undefined ? Auth.none : Auth.headers({ "chatgpt-account-id": settings.accountID }),
|
||||
),
|
||||
baseURL: "https://chatgpt.com/backend-api/codex",
|
||||
headers: settings.headers,
|
||||
providerOptions: settings.providerOptions === undefined ? undefined : { openai: settings.providerOptions },
|
||||
body: settings.body,
|
||||
limits: settings.limits,
|
||||
}),
|
||||
OpenAIResponses.route
|
||||
.with({
|
||||
auth: (settings.apiKey === undefined ? Auth.none : Auth.bearer(settings.apiKey)).andThen(
|
||||
settings.accountID === undefined ? Auth.none : Auth.headers({ "chatgpt-account-id": settings.accountID }),
|
||||
),
|
||||
endpoint: { baseURL: "https://chatgpt.com/backend-api/codex" },
|
||||
headers: settings.headers,
|
||||
providerOptions: settings.providerOptions && { openai: settings.providerOptions },
|
||||
http: { body: settings.body },
|
||||
limits: settings.limits,
|
||||
})
|
||||
.model({ id: modelID }),
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue