refactor(core): simplify model requests
This commit is contained in:
parent
116ac93ddb
commit
17f312d537
21 changed files with 56 additions and 685 deletions
|
|
@ -2,7 +2,6 @@ export * as Catalog from "./catalog"
|
|||
|
||||
import { Array, Context, Effect, Layer, Option, Order, pipe, Schema } from "effect"
|
||||
import { ModelV2 } from "./model"
|
||||
import { ModelRequest } from "./model-request"
|
||||
import { ProviderV2 } from "./provider"
|
||||
import { EventV2 } from "./event"
|
||||
import { Policy } from "./policy"
|
||||
|
|
@ -86,7 +85,8 @@ export const layer = Layer.effect(
|
|||
? { ...model.api, settings: { ...provider.api.settings, ...model.api.settings } }
|
||||
: model.api
|
||||
const request = {
|
||||
...ModelRequest.merge({ ...provider.request, generation: {}, options: {} }, model.request),
|
||||
headers: { ...provider.request.headers, ...model.request.headers },
|
||||
body: { ...provider.request.body, ...model.request.body },
|
||||
variant: model.request.variant,
|
||||
}
|
||||
return ModelV2.Info.make({
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import { define } from "../../plugin/internal"
|
|||
import { Effect } from "effect"
|
||||
import { Config } from "../../config"
|
||||
import { ModelV2 } from "../../model"
|
||||
import { ModelRequest } from "../../model-request"
|
||||
import { ProviderV2 } from "../../provider"
|
||||
|
||||
export const Plugin = define({
|
||||
|
|
@ -59,15 +58,11 @@ export const Plugin = define({
|
|||
Object.assign(provider.request.body, item.request.body)
|
||||
}
|
||||
})
|
||||
const providerApi = catalog.provider.get(providerID)?.provider.api
|
||||
const providerPackage = providerApi?.type === "aisdk" ? providerApi.package : undefined
|
||||
|
||||
for (const [id, config] of Object.entries(item.models ?? {})) {
|
||||
catalog.model.update(providerID, id, (model) => {
|
||||
if (config.family !== undefined) model.family = config.family
|
||||
if (config.name !== undefined) model.name = config.name
|
||||
if (config.api !== undefined) model.api = { ...model.api, ...config.api }
|
||||
const packageName = model.api.type === "aisdk" ? model.api.package : providerPackage
|
||||
if (config.capabilities !== undefined) {
|
||||
model.capabilities = {
|
||||
tools: config.capabilities.tools,
|
||||
|
|
@ -76,10 +71,8 @@ export const Plugin = define({
|
|||
}
|
||||
}
|
||||
if (config.request !== undefined) {
|
||||
ModelRequest.assign(model.request, {
|
||||
headers: config.request.headers,
|
||||
...ModelRequest.normalizeAiSdkOptions(packageName, config.request.body ?? {}),
|
||||
})
|
||||
Object.assign(model.request.headers, config.request.headers)
|
||||
Object.assign(model.request.body, config.request.body)
|
||||
if (config.request.variant !== undefined) model.request.variant = config.request.variant
|
||||
}
|
||||
if (config.variants !== undefined) {
|
||||
|
|
@ -90,15 +83,11 @@ export const Plugin = define({
|
|||
id: variant.id,
|
||||
headers: {},
|
||||
body: {},
|
||||
generation: {},
|
||||
options: {},
|
||||
}
|
||||
model.variants.push(existing)
|
||||
}
|
||||
ModelRequest.assign(existing, {
|
||||
headers: variant.headers,
|
||||
...ModelRequest.normalizeAiSdkOptions(packageName, variant.body ?? {}),
|
||||
})
|
||||
Object.assign(existing.headers, variant.headers)
|
||||
Object.assign(existing.body, variant.body)
|
||||
}
|
||||
}
|
||||
if (config.cost !== undefined) {
|
||||
|
|
|
|||
|
|
@ -1,102 +0,0 @@
|
|||
export * as ModelRequest from "./model-request"
|
||||
|
||||
import { ModelRequest } from "@opencode-ai/schema/model-request"
|
||||
|
||||
export const Generation = ModelRequest.Generation
|
||||
export type Generation = ModelRequest.Generation
|
||||
|
||||
export const Request = ModelRequest.Request
|
||||
export type Request = ModelRequest.Request
|
||||
|
||||
interface MutableRequest {
|
||||
headers: Record<string, string>
|
||||
body: Record<string, unknown>
|
||||
generation?: Record<string, unknown>
|
||||
options?: Record<string, unknown>
|
||||
}
|
||||
|
||||
const generationKeys = new Map<string, keyof Generation>([
|
||||
["maxOutputTokens", "maxTokens"],
|
||||
["maxTokens", "maxTokens"],
|
||||
["temperature", "temperature"],
|
||||
["topP", "topP"],
|
||||
["topK", "topK"],
|
||||
["frequencyPenalty", "frequencyPenalty"],
|
||||
["presencePenalty", "presencePenalty"],
|
||||
["seed", "seed"],
|
||||
["stopSequences", "stop"],
|
||||
["stop", "stop"],
|
||||
])
|
||||
|
||||
interface Profile {
|
||||
readonly namespace: string
|
||||
readonly semantics: ReadonlyMap<string, string>
|
||||
}
|
||||
|
||||
const profiles = new Map<string, Profile>([
|
||||
[
|
||||
"@ai-sdk/openai",
|
||||
{
|
||||
namespace: "openai",
|
||||
semantics: new Map([
|
||||
["store", "store"],
|
||||
["promptCacheKey", "promptCacheKey"],
|
||||
["reasoningEffort", "reasoningEffort"],
|
||||
["reasoningSummary", "reasoningSummary"],
|
||||
["include", "include"],
|
||||
["textVerbosity", "textVerbosity"],
|
||||
["serviceTier", "serviceTier"],
|
||||
["service_tier", "serviceTier"],
|
||||
]),
|
||||
},
|
||||
],
|
||||
[
|
||||
"@ai-sdk/openai-compatible",
|
||||
{
|
||||
namespace: "openai",
|
||||
semantics: new Map([
|
||||
["store", "store"],
|
||||
["promptCacheKey", "promptCacheKey"],
|
||||
["reasoningEffort", "reasoningEffort"],
|
||||
["reasoning_effort", "reasoningEffort"],
|
||||
]),
|
||||
},
|
||||
],
|
||||
["@ai-sdk/anthropic", { namespace: "anthropic", semantics: new Map([["thinking", "thinking"]]) }],
|
||||
])
|
||||
|
||||
export const namespace = (packageName: string) => profiles.get(packageName)?.namespace
|
||||
|
||||
export const merge = (base: Request, override: Partial<Request>) => ({
|
||||
headers: { ...base.headers, ...override.headers },
|
||||
body: { ...base.body, ...override.body },
|
||||
generation: { ...base.generation, ...override.generation },
|
||||
options: { ...base.options, ...override.options },
|
||||
})
|
||||
|
||||
export const assign = (target: MutableRequest, override: Partial<Request>) => {
|
||||
Object.assign(target.headers, override.headers)
|
||||
Object.assign(target.body, override.body)
|
||||
Object.assign((target.generation ??= {}), override.generation)
|
||||
Object.assign((target.options ??= {}), override.options)
|
||||
}
|
||||
|
||||
/** Partitions AI-SDK-shaped request options before they enter the Catalog. */
|
||||
export function normalizeAiSdkOptions(packageName: string | undefined, input: Readonly<Record<string, unknown>>) {
|
||||
const generation: Record<string, number | ReadonlyArray<string>> = {}
|
||||
const options: Record<string, unknown> = {}
|
||||
const body: Record<string, unknown> = {}
|
||||
const semantics = profiles.get(packageName ?? "")?.semantics
|
||||
|
||||
for (const [key, value] of Object.entries(input)) {
|
||||
const generationKey = generationKeys.get(key)
|
||||
if (generationKey === "stop" && Array.isArray(value) && value.every((item) => typeof item === "string"))
|
||||
generation[generationKey] = value
|
||||
else if (generationKey !== undefined && generationKey !== "stop" && typeof value === "number")
|
||||
generation[generationKey] = value
|
||||
else if (semantics?.has(key)) options[semantics.get(key)!] = value
|
||||
else body[key] = value
|
||||
}
|
||||
|
||||
return { generation, options, body }
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ import { define } from "./internal"
|
|||
import { Effect, Stream } from "effect"
|
||||
import { EventV2 } from "../event"
|
||||
import { ModelV2 } from "../model"
|
||||
import { ModelRequest } from "../model-request"
|
||||
import { ModelsDev } from "../models-dev"
|
||||
import { ProviderV2 } from "../provider"
|
||||
|
||||
|
|
@ -38,15 +37,12 @@ function cost(input: ModelsDev.Model["cost"]) {
|
|||
]
|
||||
}
|
||||
|
||||
function variants(model: ModelsDev.Model, packageName?: string) {
|
||||
return Object.entries(model.experimental?.modes ?? {}).map(([id, item]) => {
|
||||
const request = ModelRequest.normalizeAiSdkOptions(packageName, item.provider?.body ?? {})
|
||||
return {
|
||||
id: ModelV2.VariantID.make(id),
|
||||
headers: { ...(item.provider?.headers ?? {}) },
|
||||
...request,
|
||||
}
|
||||
})
|
||||
function variants(model: ModelsDev.Model) {
|
||||
return Object.entries(model.experimental?.modes ?? {}).map(([id, item]) => ({
|
||||
id: ModelV2.VariantID.make(id),
|
||||
headers: { ...(item.provider?.headers ?? {}) },
|
||||
body: { ...(item.provider?.body ?? {}) },
|
||||
}))
|
||||
}
|
||||
|
||||
export const ModelsDevPlugin = define({
|
||||
|
|
@ -115,7 +111,7 @@ export const ModelsDevPlugin = define({
|
|||
input: [...(model.modalities?.input ?? [])],
|
||||
output: [...(model.modalities?.output ?? [])],
|
||||
}
|
||||
draft.variants = variants(model, model.provider?.npm ?? item.npm)
|
||||
draft.variants = variants(model)
|
||||
draft.time.released = released(model.release_date)
|
||||
draft.cost = cost(model.cost)
|
||||
draft.status = model.status ?? "active"
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ import { EventV2 } from "../../event"
|
|||
import { Credential } from "../../credential"
|
||||
import { Integration } from "../../integration"
|
||||
import { ModelV2 } from "../../model"
|
||||
import { ModelRequest } from "../../model-request"
|
||||
import { ProviderV2 } from "../../provider"
|
||||
import { ConfigProviderV1 } from "../../v1/config/provider"
|
||||
import { ConfigProviderOptionsV1 } from "../../v1/config/provider-options"
|
||||
import { ConfigV1 } from "../../v1/config/config"
|
||||
|
||||
const defaultServer = "https://console.opencode.ai"
|
||||
|
|
@ -142,15 +142,14 @@ export const OpencodePlugin = define<HttpClient.HttpClient | EventV2.Service | S
|
|||
if (config.modalities?.input !== undefined) model.capabilities.input = [...config.modalities.input]
|
||||
if (config.modalities?.output !== undefined) model.capabilities.output = [...config.modalities.output]
|
||||
const packageName = config.provider?.npm ?? item.npm
|
||||
ModelRequest.assign(model.request, {
|
||||
headers: config.headers,
|
||||
...ModelRequest.normalizeAiSdkOptions(packageName, withoutCredentials(config.options)),
|
||||
})
|
||||
const lowerer = ConfigProviderOptionsV1.get(packageName)
|
||||
Object.assign(model.request.headers, config.headers)
|
||||
Object.assign(model.request.body, lowerer.request(withoutCredentials(config.options)))
|
||||
if (config.variants !== undefined) {
|
||||
model.variants = Object.entries(config.variants).map(([id, options]) => ({
|
||||
id: ModelV2.VariantID.make(id),
|
||||
headers: { ...(options.headers ?? {}) },
|
||||
...ModelRequest.normalizeAiSdkOptions(packageName, withoutCredentials(options)),
|
||||
body: lowerer.request(withoutCredentials(options)),
|
||||
}))
|
||||
}
|
||||
if (config.release_date !== undefined) {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import { Catalog } from "../../catalog"
|
|||
import { Credential } from "../../credential"
|
||||
import { Integration } from "../../integration"
|
||||
import { ModelV2 } from "../../model"
|
||||
import { ModelRequest } from "../../model-request"
|
||||
import { ProviderV2 } from "../../provider"
|
||||
import { SessionSchema } from "../schema"
|
||||
|
||||
|
|
@ -88,8 +87,6 @@ const apiKey = (model: ModelV2.Info, credential?: Credential.Value) => {
|
|||
}
|
||||
|
||||
const withDefaults = (model: ModelV2.Info, route: AnyRoute) => {
|
||||
const options = model.request.options ?? {}
|
||||
const namespace = model.api.type === "aisdk" ? ModelRequest.namespace(model.api.package) : undefined
|
||||
const body = model.request.body
|
||||
const httpBody = Object.hasOwn(body, "apiKey")
|
||||
? Object.fromEntries(Object.entries(body).filter(([key]) => key !== "apiKey"))
|
||||
|
|
@ -98,8 +95,6 @@ const withDefaults = (model: ModelV2.Info, route: AnyRoute) => {
|
|||
provider: model.providerID,
|
||||
endpoint: model.api.url === undefined ? undefined : { baseURL: model.api.url },
|
||||
headers: model.request.headers,
|
||||
generation: model.request.generation,
|
||||
providerOptions: namespace && Object.keys(options).length > 0 ? { [namespace]: options } : undefined,
|
||||
http: { body: httpBody },
|
||||
limits: { context: model.limit.context, output: model.limit.output },
|
||||
})
|
||||
|
|
@ -122,7 +117,8 @@ const withVariant = (
|
|||
return Effect.succeed(
|
||||
variant
|
||||
? produce(model, (draft) => {
|
||||
ModelRequest.assign(draft.request, variant)
|
||||
Object.assign(draft.request.headers, variant.headers)
|
||||
Object.assign(draft.request.body, variant.body)
|
||||
})
|
||||
: model,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import { ConfigMCPV1 } from "./mcp"
|
|||
import { ConfigPermissionV1 } from "./permission"
|
||||
import { ConfigProviderV1 } from "./provider"
|
||||
import { ConfigProviderOptionsV1 } from "./provider-options"
|
||||
import { ModelRequest } from "../../model-request"
|
||||
|
||||
const keys = new Set([
|
||||
"logLevel",
|
||||
|
|
@ -192,11 +191,7 @@ function migrateProvider(info: ConfigProviderV1.Info) {
|
|||
function migrateModel(info: typeof ConfigProviderV1.Model.Type, packageName?: string) {
|
||||
const packageID = info.provider?.npm ?? packageName
|
||||
const lowerer = ConfigProviderOptionsV1.get(packageID)
|
||||
const ingest = (options: Readonly<Record<string, unknown>>) => {
|
||||
const request = ModelRequest.normalizeAiSdkOptions(packageID, options)
|
||||
return { ...lowerer.request(request.body), ...request.generation, ...request.options }
|
||||
}
|
||||
const request = info.options && ingest(info.options)
|
||||
const request = info.options && lowerer.request(info.options)
|
||||
const costs = info.cost && [
|
||||
{
|
||||
input: info.cost.input,
|
||||
|
|
@ -241,7 +236,7 @@ function migrateModel(info: typeof ConfigProviderV1.Model.Type, packageName?: st
|
|||
info.variants &&
|
||||
Object.entries(info.variants).map(([id, options]) => ({
|
||||
id,
|
||||
body: ingest(options),
|
||||
body: lowerer.request(options),
|
||||
})),
|
||||
cost: costs,
|
||||
disabled: info.status === "deprecated" ? true : undefined,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue