refactor(core): remove ai sdk option fields
This commit is contained in:
parent
8851e4de2b
commit
06337878e4
41 changed files with 378 additions and 235 deletions
|
|
@ -34,10 +34,6 @@ export class Info extends Schema.Class<Info>("AgentV2.Info")({
|
|||
options: {
|
||||
headers: {},
|
||||
body: {},
|
||||
aisdk: {
|
||||
provider: {},
|
||||
request: {},
|
||||
},
|
||||
},
|
||||
mode: "all",
|
||||
hidden: false,
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ function wrapSSE(res: Response, ms: number, ctl: AbortController) {
|
|||
}
|
||||
|
||||
function prepareOptions(model: ModelV2.Info, pkg: string) {
|
||||
const options: Record<string, any> = { name: model.providerID, ...model.options.aisdk.provider }
|
||||
const options: Record<string, any> = { name: model.providerID, ...model.options.body }
|
||||
if (model.endpoint.type === "aisdk" && model.endpoint.url) options.baseURL = model.endpoint.url
|
||||
|
||||
const customFetch = options.fetch
|
||||
|
|
|
|||
|
|
@ -112,13 +112,6 @@ export const layer = Layer.effect(
|
|||
...provider.options.body,
|
||||
...model.options.body,
|
||||
},
|
||||
aisdk: {
|
||||
provider: {
|
||||
...provider.options.aisdk.provider,
|
||||
...model.options.aisdk.provider,
|
||||
},
|
||||
request: model.options.aisdk.request,
|
||||
},
|
||||
variant: model.options.variant,
|
||||
}
|
||||
return new ModelV2.Info({
|
||||
|
|
@ -135,9 +128,9 @@ export const layer = Layer.effect(
|
|||
}
|
||||
|
||||
const normalizeEndpoint = (item: Draft<ProviderV2.Info> | Draft<ModelV2.Info>) => {
|
||||
if (item.endpoint.type !== "aisdk" || typeof item.options.aisdk.provider.baseURL !== "string") return
|
||||
item.endpoint.url = item.options.aisdk.provider.baseURL
|
||||
delete item.options.aisdk.provider.baseURL
|
||||
if (item.endpoint.type !== "aisdk" || typeof item.options.body.baseURL !== "string") return
|
||||
item.endpoint.url = item.options.body.baseURL
|
||||
delete item.options.body.baseURL
|
||||
}
|
||||
|
||||
const state = State.create<Data, Editor>({
|
||||
|
|
|
|||
|
|
@ -37,8 +37,6 @@ export const Plugin = PluginV2.define({
|
|||
if (item.options !== undefined) {
|
||||
Object.assign(agent.options.headers, item.options.headers ?? {})
|
||||
Object.assign(agent.options.body, item.options.body ?? {})
|
||||
Object.assign(agent.options.aisdk.provider, item.options.aisdk?.provider ?? {})
|
||||
Object.assign(agent.options.aisdk.request, item.options.aisdk?.request ?? {})
|
||||
}
|
||||
if (item.system !== undefined) agent.system = item.system
|
||||
if (item.description !== undefined) agent.description = item.description
|
||||
|
|
|
|||
|
|
@ -27,8 +27,6 @@ export const Plugin = PluginV2.define({
|
|||
if (item.options !== undefined) {
|
||||
Object.assign(provider.options.headers, item.options.headers ?? {})
|
||||
Object.assign(provider.options.body, item.options.body ?? {})
|
||||
Object.assign(provider.options.aisdk.provider, item.options.aisdk?.provider ?? {})
|
||||
Object.assign(provider.options.aisdk.request, item.options.aisdk?.request ?? {})
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -48,8 +46,6 @@ export const Plugin = PluginV2.define({
|
|||
if (config.options !== undefined) {
|
||||
Object.assign(model.options.headers, config.options.headers ?? {})
|
||||
Object.assign(model.options.body, config.options.body ?? {})
|
||||
Object.assign(model.options.aisdk.provider, config.options.aisdk?.provider ?? {})
|
||||
Object.assign(model.options.aisdk.request, config.options.aisdk?.request ?? {})
|
||||
if (config.options.variant !== undefined) model.options.variant = config.options.variant
|
||||
}
|
||||
if (config.variants !== undefined) {
|
||||
|
|
@ -60,17 +56,11 @@ export const Plugin = PluginV2.define({
|
|||
id: variant.id,
|
||||
headers: {},
|
||||
body: {},
|
||||
aisdk: {
|
||||
provider: {},
|
||||
request: {},
|
||||
},
|
||||
}
|
||||
model.variants.push(existing)
|
||||
}
|
||||
Object.assign(existing.headers, variant.headers ?? {})
|
||||
Object.assign(existing.body, variant.body ?? {})
|
||||
Object.assign(existing.aisdk.provider, variant.aisdk?.provider ?? {})
|
||||
Object.assign(existing.aisdk.request, variant.aisdk?.request ?? {})
|
||||
}
|
||||
}
|
||||
if (config.cost !== undefined) {
|
||||
|
|
|
|||
|
|
@ -7,10 +7,6 @@ import { ModelV2 } from "../model"
|
|||
export class Options extends Schema.Class<Options>("ConfigV2.Provider.Options")({
|
||||
headers: Schema.Record(Schema.String, Schema.String).pipe(Schema.optional),
|
||||
body: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
|
||||
aisdk: Schema.Struct({
|
||||
provider: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
|
||||
request: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
|
||||
}).pipe(Schema.optional),
|
||||
}) {}
|
||||
|
||||
class Cache extends Schema.Class<Cache>("ConfigV2.Model.Cost.Cache")({
|
||||
|
|
|
|||
|
|
@ -85,10 +85,6 @@ export class Info extends Schema.Class<Info>("ModelV2.Info")({
|
|||
options: {
|
||||
headers: {},
|
||||
body: {},
|
||||
aisdk: {
|
||||
provider: {},
|
||||
request: {},
|
||||
},
|
||||
},
|
||||
variants: [],
|
||||
time: {
|
||||
|
|
|
|||
|
|
@ -32,10 +32,10 @@ export const AccountPlugin = PluginV2.define({
|
|||
service: account.serviceID,
|
||||
}
|
||||
if (account.credential.type === "api") {
|
||||
provider.options.aisdk.provider.apiKey = account.credential.key
|
||||
Object.assign(provider.options.aisdk.provider, account.credential.metadata ?? {})
|
||||
provider.options.body.apiKey = account.credential.key
|
||||
Object.assign(provider.options.body, account.credential.metadata ?? {})
|
||||
}
|
||||
if (account.credential.type === "oauth") provider.options.aisdk.provider.apiKey = account.credential.access
|
||||
if (account.credential.type === "oauth") provider.options.body.apiKey = account.credential.access
|
||||
})
|
||||
}
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -43,10 +43,6 @@ function variants(model: ModelsDev.Model) {
|
|||
id: ModelV2.VariantID.make(id),
|
||||
headers: { ...(item.provider?.headers ?? {}) },
|
||||
body: { ...(item.provider?.body ?? {}) },
|
||||
aisdk: {
|
||||
provider: {},
|
||||
request: {},
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,11 +56,11 @@ export const AmazonBedrockPlugin = PluginV2.define({
|
|||
if (item.provider.endpoint.package !== "@ai-sdk/amazon-bedrock") continue
|
||||
evt.provider.update(item.provider.id, (provider) => {
|
||||
if (provider.endpoint.type !== "aisdk") return
|
||||
if (typeof provider.options.aisdk.provider.endpoint !== "string") return
|
||||
if (typeof provider.options.body.endpoint !== "string") return
|
||||
// The AI SDK expects a base URL, but users configure Bedrock private/VPC
|
||||
// endpoints as `endpoint`; move it into the catalog endpoint URL once.
|
||||
provider.endpoint.url = provider.options.aisdk.provider.endpoint
|
||||
delete provider.options.aisdk.provider.endpoint
|
||||
provider.endpoint.url = provider.options.body.endpoint
|
||||
delete provider.options.body.endpoint
|
||||
})
|
||||
}
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -18,12 +18,12 @@ export const AzurePlugin = PluginV2.define({
|
|||
for (const item of evt.provider.list()) {
|
||||
if (item.provider.endpoint.type !== "aisdk") continue
|
||||
if (item.provider.endpoint.package !== "@ai-sdk/azure") continue
|
||||
const configured = item.provider.options.aisdk.provider.resourceName
|
||||
const configured = item.provider.options.body.resourceName
|
||||
const resourceName =
|
||||
typeof configured === "string" && configured.trim() !== "" ? configured : process.env.AZURE_RESOURCE_NAME
|
||||
if (!resourceName) continue
|
||||
evt.provider.update(item.provider.id, (provider) => {
|
||||
provider.options.aisdk.provider.resourceName = resourceName
|
||||
provider.options.body.resourceName = resourceName
|
||||
})
|
||||
}
|
||||
}),
|
||||
|
|
@ -63,7 +63,7 @@ export const AzureCognitiveServicesPlugin = PluginV2.define({
|
|||
if (item.provider.endpoint.package !== "@ai-sdk/openai-compatible") continue
|
||||
if (!item.provider.id.includes("azure-cognitive-services")) continue
|
||||
evt.provider.update(item.provider.id, (provider) => {
|
||||
provider.options.aisdk.provider.baseURL = `https://${resourceName}.cognitiveservices.azure.com/openai`
|
||||
provider.options.body.baseURL = `https://${resourceName}.cognitiveservices.azure.com/openai`
|
||||
})
|
||||
}
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export const CloudflareWorkersAIPlugin = PluginV2.define({
|
|||
evt.provider.update(item.provider.id, (provider) => {
|
||||
if (provider.endpoint.type !== "aisdk") return
|
||||
if (provider.endpoint.url) return
|
||||
const accountId = resolveAccountId(provider.options.aisdk.provider)
|
||||
const accountId = resolveAccountId(provider.options.body)
|
||||
if (accountId) provider.endpoint.url = workersEndpoint(accountId)
|
||||
})
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -37,12 +37,12 @@ export const GitLabPlugin = PluginV2.define({
|
|||
if (evt.model.apiID.startsWith("duo-workflow-")) {
|
||||
const gitlab = yield* Effect.promise(() => import("gitlab-ai-provider")).pipe(Effect.orDie)
|
||||
const workflowRef =
|
||||
typeof evt.model.options.aisdk.request.workflowRef === "string"
|
||||
? evt.model.options.aisdk.request.workflowRef
|
||||
typeof evt.model.options.body.workflowRef === "string"
|
||||
? evt.model.options.body.workflowRef
|
||||
: undefined
|
||||
const workflowDefinition =
|
||||
typeof evt.model.options.aisdk.request.workflowDefinition === "string"
|
||||
? evt.model.options.aisdk.request.workflowDefinition
|
||||
typeof evt.model.options.body.workflowDefinition === "string"
|
||||
? evt.model.options.body.workflowDefinition
|
||||
: undefined
|
||||
const language = evt.sdk.workflowChat(
|
||||
gitlab.isWorkflowModel(evt.model.apiID) ? evt.model.apiID : "duo-workflow",
|
||||
|
|
|
|||
|
|
@ -66,16 +66,16 @@ export const GoogleVertexPlugin = PluginV2.define({
|
|||
!item.provider.endpoint.package.includes("@ai-sdk/openai-compatible")
|
||||
)
|
||||
continue
|
||||
const project = resolveProject(item.provider.options.aisdk.provider)
|
||||
const location = String(resolveLocation(item.provider.options.aisdk.provider))
|
||||
const project = resolveProject(item.provider.options.body)
|
||||
const location = String(resolveLocation(item.provider.options.body))
|
||||
evt.provider.update(item.provider.id, (provider) => {
|
||||
if (project) provider.options.aisdk.provider.project = project
|
||||
provider.options.aisdk.provider.location = location
|
||||
if (project) provider.options.body.project = project
|
||||
provider.options.body.location = location
|
||||
if (provider.endpoint.type === "aisdk" && provider.endpoint.url) {
|
||||
provider.endpoint.url = replaceVertexVars(provider.endpoint.url, project, location)
|
||||
}
|
||||
if (provider.endpoint.type === "aisdk" && provider.endpoint.package.includes("@ai-sdk/openai-compatible")) {
|
||||
provider.options.aisdk.provider.fetch = authFetch(provider.options.aisdk.provider.fetch)
|
||||
provider.options.body.fetch = authFetch(provider.options.body.fetch)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -114,18 +114,18 @@ export const GoogleVertexAnthropicPlugin = PluginV2.define({
|
|||
if (item.provider.endpoint.type !== "aisdk") continue
|
||||
if (item.provider.endpoint.package !== "@ai-sdk/google-vertex/anthropic") continue
|
||||
const project =
|
||||
item.provider.options.aisdk.provider.project ??
|
||||
item.provider.options.body.project ??
|
||||
process.env.GOOGLE_CLOUD_PROJECT ??
|
||||
process.env.GCP_PROJECT ??
|
||||
process.env.GCLOUD_PROJECT
|
||||
const location =
|
||||
item.provider.options.aisdk.provider.location ??
|
||||
item.provider.options.body.location ??
|
||||
process.env.GOOGLE_CLOUD_LOCATION ??
|
||||
process.env.VERTEX_LOCATION ??
|
||||
"global"
|
||||
evt.provider.update(item.provider.id, (provider) => {
|
||||
if (project) provider.options.aisdk.provider.project = project
|
||||
provider.options.aisdk.provider.location = location
|
||||
if (project) provider.options.body.project = project
|
||||
provider.options.body.location = location
|
||||
})
|
||||
}
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ export const OpencodePlugin = PluginV2.define({
|
|||
hasKey = Boolean(
|
||||
process.env.OPENCODE_API_KEY ||
|
||||
item.provider.env.some((env) => process.env[env]) ||
|
||||
item.provider.options.aisdk.provider.apiKey ||
|
||||
item.provider.options.body.apiKey ||
|
||||
(item.provider.enabled && item.provider.enabled.via === "account"),
|
||||
)
|
||||
evt.provider.update(item.provider.id, (provider) => {
|
||||
if (!hasKey) provider.options.aisdk.provider.apiKey = "public"
|
||||
if (!hasKey) provider.options.body.apiKey = "public"
|
||||
})
|
||||
if (hasKey) return
|
||||
for (const model of item.models.values()) {
|
||||
|
|
|
|||
|
|
@ -72,10 +72,6 @@ export type Endpoint = typeof Endpoint.Type
|
|||
export const Options = Schema.Struct({
|
||||
headers: Schema.Record(Schema.String, Schema.String),
|
||||
body: Schema.Record(Schema.String, Schema.Any),
|
||||
aisdk: Schema.Struct({
|
||||
provider: Schema.Record(Schema.String, Schema.Any),
|
||||
request: Schema.Record(Schema.String, Schema.Any),
|
||||
}),
|
||||
})
|
||||
export type Options = typeof Options.Type
|
||||
|
||||
|
|
@ -113,10 +109,6 @@ export class Info extends Schema.Class<Info>("ProviderV2.Info")({
|
|||
options: {
|
||||
headers: {},
|
||||
body: {},
|
||||
aisdk: {
|
||||
provider: {},
|
||||
request: {},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { ConfigAgentV1 } from "./agent"
|
|||
import { ConfigMCPV1 } from "./mcp"
|
||||
import { ConfigPermissionV1 } from "./permission"
|
||||
import { ConfigProviderV1 } from "./provider"
|
||||
import { ConfigProviderOptionsV1 } from "./provider-options"
|
||||
|
||||
const keys = new Set([
|
||||
"logLevel",
|
||||
|
|
@ -99,7 +100,7 @@ function agents(info: typeof ConfigV1.Info.Type) {
|
|||
...Object.entries(info.mode ?? {}).map(([name, agent]) => [name, { ...agent, mode: "primary" as const }] as const),
|
||||
]
|
||||
if (!entries.length) return undefined
|
||||
return Object.fromEntries(entries.map(([name, agent]) => [name, migrateAgent(agent)]))
|
||||
return Object.fromEntries(entries.flatMap(([name, agent]) => (agent ? [[name, migrateAgent(agent)]] : [])))
|
||||
}
|
||||
|
||||
function migrateAgent(info: ConfigAgentV1.Info) {
|
||||
|
|
@ -160,22 +161,26 @@ function providers(info?: Readonly<Record<string, ConfigProviderV1.Info>>) {
|
|||
}
|
||||
|
||||
function migrateProvider(info: ConfigProviderV1.Info) {
|
||||
const lowerer = ConfigProviderOptionsV1.get(info.npm)
|
||||
const options = lowerer.provider(info.options ?? {})
|
||||
return {
|
||||
name: info.name,
|
||||
env: info.env,
|
||||
endpoint: info.npm && {
|
||||
type: "aisdk" as const,
|
||||
package: info.npm,
|
||||
url: info.api ?? (typeof info.options?.baseURL === "string" ? info.options.baseURL : undefined),
|
||||
},
|
||||
options: info.options && { body: info.options },
|
||||
endpoint: info.npm
|
||||
? {
|
||||
type: "aisdk" as const,
|
||||
package: info.npm,
|
||||
url: info.api ?? options.url,
|
||||
}
|
||||
: undefined,
|
||||
options: info.options && { headers: options.headers, body: options.body },
|
||||
models:
|
||||
info.models &&
|
||||
Object.fromEntries(Object.entries(info.models).map(([name, model]) => [name, migrateModel(model)])),
|
||||
Object.fromEntries(Object.entries(info.models).map(([name, model]) => [name, migrateModel(model, info.npm)])),
|
||||
}
|
||||
}
|
||||
|
||||
function migrateModel(info: typeof ConfigProviderV1.Model.Type) {
|
||||
function migrateModel(info: typeof ConfigProviderV1.Model.Type, packageName?: string) {
|
||||
const costs = info.cost && [
|
||||
{
|
||||
input: info.cost.input,
|
||||
|
|
@ -197,16 +202,33 @@ function migrateModel(info: typeof ConfigProviderV1.Model.Type) {
|
|||
info.tool_call !== undefined || info.modalities?.input !== undefined || info.modalities?.output !== undefined
|
||||
? { tools: info.tool_call ?? false, input: info.modalities?.input ?? [], output: info.modalities?.output ?? [] }
|
||||
: undefined
|
||||
const lowerer = ConfigProviderOptionsV1.get(info.provider?.npm ?? packageName)
|
||||
return {
|
||||
api_id: info.id,
|
||||
family: info.family,
|
||||
name: info.name,
|
||||
endpoint: info.provider?.npm && { type: "aisdk" as const, package: info.provider.npm, url: info.provider.api },
|
||||
endpoint: info.provider?.npm
|
||||
? { type: "aisdk" as const, package: info.provider.npm, url: info.provider.api }
|
||||
: undefined,
|
||||
capabilities,
|
||||
options: (info.headers || info.options) && { headers: info.headers, body: info.options },
|
||||
variants: info.variants && Object.entries(info.variants).map(([id, options]) => ({ id, body: options })),
|
||||
options: (info.headers || info.options) && {
|
||||
headers: info.headers,
|
||||
body: info.options && lowerer.request(info.options),
|
||||
},
|
||||
variants:
|
||||
info.variants &&
|
||||
Object.entries(info.variants).map(([id, options]) => ({ id, body: lowerer.request(options) })),
|
||||
cost: costs,
|
||||
disabled: info.status === "deprecated" ? true : undefined,
|
||||
limit: info.limit,
|
||||
limit:
|
||||
info.limit && {
|
||||
context: int(info.limit.context),
|
||||
input: info.limit.input === undefined ? undefined : int(info.limit.input),
|
||||
output: int(info.limit.output),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function int(value: number) {
|
||||
return Math.max(Number.MIN_SAFE_INTEGER, Math.min(Number.MAX_SAFE_INTEGER, Math.trunc(value)))
|
||||
}
|
||||
|
|
|
|||
193
packages/core/src/v1/config/provider-options.ts
Normal file
193
packages/core/src/v1/config/provider-options.ts
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
export * as ConfigProviderOptionsV1 from "./provider-options"
|
||||
|
||||
type Options = Readonly<Record<string, unknown>>
|
||||
|
||||
export interface ProviderResult {
|
||||
readonly headers?: Record<string, string>
|
||||
readonly body?: Record<string, unknown>
|
||||
readonly url?: string
|
||||
}
|
||||
|
||||
export interface Lowerer {
|
||||
readonly provider: (options: Options) => ProviderResult
|
||||
readonly request: (options: Options) => Record<string, unknown>
|
||||
}
|
||||
|
||||
export function get(packageName?: string): Lowerer {
|
||||
return lowerers[packageName ?? ""] ?? raw
|
||||
}
|
||||
|
||||
const raw: Lowerer = {
|
||||
provider: body,
|
||||
request: clone,
|
||||
}
|
||||
|
||||
const openai: Lowerer = {
|
||||
provider(options) {
|
||||
return {
|
||||
url: string(options.baseURL),
|
||||
headers: compact({
|
||||
Authorization: bearer(options.apiKey),
|
||||
"OpenAI-Organization": string(options.organization),
|
||||
"OpenAI-Project": string(options.project),
|
||||
...headers(options.headers),
|
||||
}),
|
||||
body: omit(options, ["apiKey", "baseURL", "organization", "project", "headers", "name", "fetch"]),
|
||||
}
|
||||
},
|
||||
request: snake,
|
||||
}
|
||||
|
||||
const anthropic: Lowerer = {
|
||||
provider(options) {
|
||||
return {
|
||||
url: string(options.baseURL),
|
||||
headers: compact({
|
||||
"x-api-key": string(options.apiKey),
|
||||
Authorization: options.authToken ? bearer(options.authToken) : undefined,
|
||||
...headers(options.headers),
|
||||
}),
|
||||
body: omit(options, ["apiKey", "authToken", "baseURL", "headers", "name", "fetch", "generateId"]),
|
||||
}
|
||||
},
|
||||
request(options) {
|
||||
const result = snake(options)
|
||||
if (options.effort !== undefined || options.taskBudget !== undefined) {
|
||||
result.output_config = compactUnknown({ effort: options.effort, task_budget: options.taskBudget })
|
||||
delete result.effort
|
||||
delete result.task_budget
|
||||
}
|
||||
if (isRecord(options.metadata) && options.metadata.userId !== undefined) {
|
||||
result.metadata = { ...options.metadata, user_id: options.metadata.userId }
|
||||
delete (result.metadata as Record<string, unknown>).userId
|
||||
}
|
||||
return result
|
||||
},
|
||||
}
|
||||
|
||||
const google: Lowerer = {
|
||||
provider(options) {
|
||||
return {
|
||||
url: string(options.baseURL),
|
||||
headers: compact({ "x-goog-api-key": string(options.apiKey), ...headers(options.headers) }),
|
||||
body: omit(options, ["apiKey", "baseURL", "headers", "name", "fetch", "generateId"]),
|
||||
}
|
||||
},
|
||||
request(options) {
|
||||
const generationConfig = pick(options, ["thinkingConfig", "responseModalities", "mediaResolution", "imageConfig"])
|
||||
return {
|
||||
...omit(options, ["thinkingConfig", "responseModalities", "mediaResolution", "imageConfig"]),
|
||||
...(Object.keys(generationConfig).length ? { generationConfig } : {}),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const azure: Lowerer = {
|
||||
provider(options) {
|
||||
return {
|
||||
url: string(options.baseURL),
|
||||
headers: compact({ "api-key": string(options.apiKey), ...headers(options.headers) }),
|
||||
body: omit(options, ["apiKey", "baseURL", "headers", "name", "fetch", "tokenProvider"]),
|
||||
}
|
||||
},
|
||||
request: openai.request,
|
||||
}
|
||||
|
||||
const bedrock: Lowerer = {
|
||||
provider(options) {
|
||||
return { headers: headers(options.headers), body: omit(options, ["headers", "fetch"]) }
|
||||
},
|
||||
request(options) {
|
||||
return { additionalModelRequestFields: clone(options) }
|
||||
},
|
||||
}
|
||||
|
||||
const openaiCompatible: Lowerer = {
|
||||
provider(options) {
|
||||
return { url: string(options.baseURL), headers: headers(options.headers), body: omit(options, ["baseURL", "headers", "name", "fetch"]) }
|
||||
},
|
||||
request(options) {
|
||||
const result = clone(options)
|
||||
if (options.reasoningEffort !== undefined) {
|
||||
result.reasoning_effort = options.reasoningEffort
|
||||
delete result.reasoningEffort
|
||||
}
|
||||
return result
|
||||
},
|
||||
}
|
||||
|
||||
const lowerers: Readonly<Record<string, Lowerer>> = {
|
||||
"@ai-sdk/openai": openai,
|
||||
"@ai-sdk/anthropic": anthropic,
|
||||
"@ai-sdk/google-vertex/anthropic": anthropic,
|
||||
"@ai-sdk/google": google,
|
||||
"@ai-sdk/google-vertex": google,
|
||||
"@ai-sdk/azure": azure,
|
||||
"@ai-sdk/amazon-bedrock": bedrock,
|
||||
"@ai-sdk/openai-compatible": openaiCompatible,
|
||||
"@ai-sdk/cerebras": openaiCompatible,
|
||||
"@ai-sdk/deepinfra": openaiCompatible,
|
||||
"@ai-sdk/groq": openaiCompatible,
|
||||
"@ai-sdk/mistral": openaiCompatible,
|
||||
"@ai-sdk/togetherai": openaiCompatible,
|
||||
"@ai-sdk/xai": openaiCompatible,
|
||||
"@openrouter/ai-sdk-provider": openaiCompatible,
|
||||
"ai-gateway-provider": openaiCompatible,
|
||||
"venice-ai-sdk-provider": openaiCompatible,
|
||||
}
|
||||
|
||||
function body(options: Options): ProviderResult {
|
||||
return { body: clone(options) }
|
||||
}
|
||||
|
||||
function snake(options: Options) {
|
||||
return Object.fromEntries(Object.entries(options).map(([key, value]) => [snakeKey(key), snakeValue(value)]))
|
||||
}
|
||||
|
||||
function snakeValue(value: unknown): unknown {
|
||||
if (Array.isArray(value)) return value.map(snakeValue)
|
||||
if (!isRecord(value)) return value
|
||||
return Object.fromEntries(Object.entries(value).map(([key, value]) => [snakeKey(key), snakeValue(value)]))
|
||||
}
|
||||
|
||||
function snakeKey(key: string) {
|
||||
return key.replace(/[A-Z]/g, (match) => "_" + match.toLowerCase())
|
||||
}
|
||||
|
||||
function clone(options: Options) {
|
||||
return { ...options }
|
||||
}
|
||||
|
||||
function omit(options: Options, keys: ReadonlyArray<string>) {
|
||||
return Object.fromEntries(Object.entries(options).filter(([key]) => !keys.includes(key)))
|
||||
}
|
||||
|
||||
function pick(options: Options, keys: ReadonlyArray<string>) {
|
||||
return Object.fromEntries(Object.entries(options).filter(([key]) => keys.includes(key)))
|
||||
}
|
||||
|
||||
function headers(input: unknown) {
|
||||
if (!isRecord(input)) return undefined
|
||||
return Object.fromEntries(Object.entries(input).filter((entry): entry is [string, string] => typeof entry[1] === "string"))
|
||||
}
|
||||
|
||||
function compact(input: Record<string, string | undefined>) {
|
||||
const entries = Object.entries(input).filter((entry): entry is [string, string] => entry[1] !== undefined)
|
||||
return entries.length ? Object.fromEntries(entries) : undefined
|
||||
}
|
||||
|
||||
function compactUnknown(input: Record<string, unknown>) {
|
||||
return Object.fromEntries(Object.entries(input).filter((entry) => entry[1] !== undefined))
|
||||
}
|
||||
|
||||
function string(input: unknown) {
|
||||
return typeof input === "string" && input ? input : undefined
|
||||
}
|
||||
|
||||
function bearer(input: unknown) {
|
||||
return typeof input === "string" && input ? `Bearer ${input}` : undefined
|
||||
}
|
||||
|
||||
function isRecord(input: unknown): input is Record<string, unknown> {
|
||||
return typeof input === "object" && input !== null && !Array.isArray(input)
|
||||
}
|
||||
|
|
@ -33,8 +33,8 @@ function context(
|
|||
id: providerID,
|
||||
enabled: provider.enabled,
|
||||
apiKey:
|
||||
typeof provider.options.aisdk.provider.apiKey === "string"
|
||||
? provider.options.aisdk.provider.apiKey
|
||||
typeof provider.options.body.apiKey === "string"
|
||||
? provider.options.body.apiKey
|
||||
: undefined,
|
||||
})
|
||||
},
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ describe("CatalogV2", () => {
|
|||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://default.example.com",
|
||||
}
|
||||
provider.options.aisdk.provider.baseURL = "https://override.example.com"
|
||||
provider.options.body.baseURL = "https://override.example.com"
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ describe("CatalogV2", () => {
|
|||
})
|
||||
catalog.model.update(providerID, modelID, (model) => {
|
||||
model.endpoint = { type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://model.example.com" }
|
||||
model.options.aisdk.provider.baseURL = "https://override.example.com"
|
||||
model.options.body.baseURL = "https://override.example.com"
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -117,14 +117,14 @@ describe("CatalogV2", () => {
|
|||
if (!item) return
|
||||
seen.push(item.provider.endpoint.type)
|
||||
if (item?.provider.endpoint.type === "aisdk") seen.push(item.provider.endpoint.url)
|
||||
seen.push(item?.provider.options.aisdk.provider.baseURL)
|
||||
seen.push(item?.provider.options.body.baseURL)
|
||||
}),
|
||||
}),
|
||||
})
|
||||
yield* transform((catalog) =>
|
||||
catalog.provider.update(providerID, (provider) => {
|
||||
provider.endpoint = { type: "aisdk", package: "@ai-sdk/openai-compatible" }
|
||||
provider.options.aisdk.provider.baseURL = "https://provider.example.com"
|
||||
provider.options.body.baseURL = "https://provider.example.com"
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -199,22 +199,22 @@ describe("CatalogV2", () => {
|
|||
provider.options.headers.provider = "provider"
|
||||
provider.options.headers.shared = "provider"
|
||||
provider.options.body.provider = true
|
||||
provider.options.aisdk.provider.provider = true
|
||||
provider.options.body.provider = true
|
||||
})
|
||||
catalog.model.update(providerID, modelID, (model) => {
|
||||
model.options.headers.model = "model"
|
||||
model.options.headers.shared = "model"
|
||||
model.options.body.model = true
|
||||
model.options.aisdk.provider.model = true
|
||||
model.options.aisdk.request.request = true
|
||||
model.options.body.model = true
|
||||
model.options.body.request = true
|
||||
})
|
||||
})
|
||||
|
||||
const model = yield* catalog.model.get(providerID, modelID)
|
||||
expect(model.options.headers).toEqual({ provider: "provider", shared: "model", model: "model" })
|
||||
expect(model.options.body).toEqual({ provider: true, model: true })
|
||||
expect(model.options.aisdk.provider).toEqual({ provider: true, model: true })
|
||||
expect(model.options.aisdk.request).toEqual({ request: true })
|
||||
expect(model.options.body).toEqual({ provider: true, model: true })
|
||||
expect(model.options.body).toEqual({ request: true })
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -109,8 +109,7 @@ describe("ConfigAgentPlugin.Plugin", () => {
|
|||
steps: 12,
|
||||
options: {
|
||||
headers: { first: "one", shared: "first" },
|
||||
body: { enabled: true },
|
||||
aisdk: { provider: { profile: "review" }, request: { effort: "medium" } },
|
||||
body: { enabled: true, profile: "review", effort: "medium" },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -123,8 +122,7 @@ describe("ConfigAgentPlugin.Plugin", () => {
|
|||
reviewer: {
|
||||
options: {
|
||||
headers: { shared: "last", second: "two" },
|
||||
body: { retries: 2 },
|
||||
aisdk: { request: { effort: "high" } },
|
||||
body: { retries: 2, effort: "high" },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -151,8 +149,7 @@ describe("ConfigAgentPlugin.Plugin", () => {
|
|||
})
|
||||
expect(reviewer.options).toEqual({
|
||||
headers: { first: "one", shared: "last", second: "two" },
|
||||
body: { enabled: true, retries: 2 },
|
||||
aisdk: { provider: { profile: "review" }, request: { effort: "high" } },
|
||||
body: { enabled: true, profile: "review", retries: 2, effort: "high" },
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import path from "path"
|
||||
import fs from "fs/promises"
|
||||
import { describe, expect } from "bun:test"
|
||||
import { Effect, Layer } from "effect"
|
||||
import { Effect, Layer, Schema } from "effect"
|
||||
import { FastCheck } from "effect/testing"
|
||||
import { Config } from "@opencode-ai/core/config"
|
||||
import { ConfigProvider } from "@opencode-ai/core/config/provider"
|
||||
import { ConfigMigrateV1 } from "@opencode-ai/core/v1/config/migrate"
|
||||
import { ConfigV1 } from "@opencode-ai/core/v1/config/config"
|
||||
import { FSUtil } from "@opencode-ai/core/fs-util"
|
||||
import { Global } from "@opencode-ai/core/global"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
|
|
@ -45,10 +47,6 @@ const provider = {
|
|||
options: {
|
||||
headers: {},
|
||||
body: {},
|
||||
aisdk: {
|
||||
provider: {},
|
||||
request: {},
|
||||
},
|
||||
},
|
||||
models: {},
|
||||
}
|
||||
|
|
@ -62,6 +60,17 @@ describe("Config", () => {
|
|||
}),
|
||||
)
|
||||
|
||||
it.effect("migrates arbitrary v1 configuration into valid v2 configuration", () =>
|
||||
Effect.sync(() => {
|
||||
FastCheck.assert(
|
||||
FastCheck.property(Schema.toArbitrary(ConfigV1.Info), (info) => {
|
||||
Schema.decodeUnknownSync(Config.Info)(ConfigMigrateV1.migrate(info), { errors: "all" })
|
||||
}),
|
||||
{ numRuns: 100 },
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("returns an empty configuration when directory files do not exist", () =>
|
||||
Effect.acquireRelease(
|
||||
Effect.promise(() => tmpdir()),
|
||||
|
|
@ -188,7 +197,7 @@ describe("Config", () => {
|
|||
variant: "high",
|
||||
options: {
|
||||
headers: { "x-agent": "reviewer" },
|
||||
aisdk: { request: { reasoningEffort: "high" } },
|
||||
body: { reasoningEffort: "high" },
|
||||
},
|
||||
description: "Review changes for correctness",
|
||||
system: "Find regressions.",
|
||||
|
|
@ -271,7 +280,7 @@ describe("Config", () => {
|
|||
variant: "high",
|
||||
options: {
|
||||
headers: { "x-agent": "reviewer" },
|
||||
aisdk: { request: { reasoningEffort: "high" } },
|
||||
body: { reasoningEffort: "high" },
|
||||
},
|
||||
description: "Review changes for correctness",
|
||||
system: "Find regressions.",
|
||||
|
|
@ -379,6 +388,24 @@ describe("Config", () => {
|
|||
skills: { paths: ["./skills"], urls: ["https://example.com/.well-known/skills/"] },
|
||||
reference: { docs: { path: "../docs" } },
|
||||
attachment: { image: { auto_resize: false, max_width: 1200 } },
|
||||
provider: {
|
||||
custom: {
|
||||
options: { apiKey: "secret" },
|
||||
models: {
|
||||
model: {
|
||||
options: { reasoningEffort: "high" },
|
||||
variants: { fast: { temperature: 0.2 } },
|
||||
},
|
||||
},
|
||||
},
|
||||
openai: {
|
||||
npm: "@ai-sdk/openai",
|
||||
options: { apiKey: "secret", organization: "org" },
|
||||
models: {
|
||||
model: { options: { reasoningEffort: "high", serviceTier: "priority" } },
|
||||
},
|
||||
},
|
||||
},
|
||||
compaction: { auto: true, tail_turns: 3, preserve_recent_tokens: 2000, reserved: 10000 },
|
||||
experimental: { mcp_timeout: 5000 },
|
||||
mcp: {
|
||||
|
|
@ -420,6 +447,19 @@ describe("Config", () => {
|
|||
expect(documents[0]?.info.skills).toEqual(["./skills", "https://example.com/.well-known/skills/"])
|
||||
expect(documents[0]?.info.references).toEqual({ docs: { path: "../docs" } })
|
||||
expect(documents[0]?.info.attachments).toEqual({ image: { auto_resize: false, max_width: 1200 } })
|
||||
expect(documents[0]?.info.providers?.custom).toMatchObject({
|
||||
options: { body: { apiKey: "secret" } },
|
||||
models: {
|
||||
model: {
|
||||
options: { body: { reasoningEffort: "high" } },
|
||||
variants: [{ id: "fast", body: { temperature: 0.2 } }],
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(documents[0]?.info.providers?.openai).toMatchObject({
|
||||
options: { headers: { Authorization: "Bearer secret", "OpenAI-Organization": "org" } },
|
||||
models: { model: { options: { body: { reasoning_effort: "high", service_tier: "priority" } } } },
|
||||
})
|
||||
expect(documents[0]?.info.compaction).toEqual({
|
||||
auto: true,
|
||||
prune: undefined,
|
||||
|
|
|
|||
|
|
@ -30,8 +30,7 @@ describe("AmazonBedrockPlugin", () => {
|
|||
endpoint: { type: "aisdk", package: "@ai-sdk/amazon-bedrock" },
|
||||
options: {
|
||||
headers: {},
|
||||
body: {},
|
||||
aisdk: { provider: { endpoint: "https://bedrock.example" }, request: {} },
|
||||
body: { endpoint: "https://bedrock.example" },
|
||||
},
|
||||
})
|
||||
catalog.provider.update(bedrock.id, (item) => {
|
||||
|
|
@ -45,7 +44,7 @@ describe("AmazonBedrockPlugin", () => {
|
|||
package: "@ai-sdk/amazon-bedrock",
|
||||
url: "https://bedrock.example",
|
||||
})
|
||||
expect(result.options.aisdk.provider.endpoint).toBeUndefined()
|
||||
expect(result.options.body.endpoint).toBeUndefined()
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ describe("AnthropicPlugin", () => {
|
|||
yield* transform((catalog) => {
|
||||
const item = provider("anthropic", {
|
||||
endpoint: { type: "aisdk", package: "@ai-sdk/anthropic" },
|
||||
options: { headers: { Existing: "1" }, body: {}, aisdk: { provider: {}, request: {} } },
|
||||
options: { headers: { Existing: "1" }, body: {}, },
|
||||
})
|
||||
catalog.provider.update(item.id, (draft) => {
|
||||
draft.endpoint = item.endpoint
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ describe("AzureCognitiveServicesPlugin", () => {
|
|||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://cognitive.cognitiveservices.azure.com/openai",
|
||||
})
|
||||
expect(result.options.aisdk.provider.baseURL).toBeUndefined()
|
||||
expect(result.options.aisdk.provider.resourceName).toBeUndefined()
|
||||
expect(result.options.body.baseURL).toBeUndefined()
|
||||
expect(result.options.body.resourceName).toBeUndefined()
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -52,9 +52,9 @@ describe("AzureCognitiveServicesPlugin", () => {
|
|||
})
|
||||
const azure = yield* catalog.provider.get(ProviderV2.ID.make("azure-cognitive-services"))
|
||||
const openai = yield* catalog.provider.get(ProviderV2.ID.openai)
|
||||
expect(azure.options.aisdk.provider.baseURL).toBeUndefined()
|
||||
expect(azure.options.body.baseURL).toBeUndefined()
|
||||
expect(azure.endpoint).toEqual({ type: "aisdk", package: "@ai-sdk/openai-compatible" })
|
||||
expect(openai.options.aisdk.provider.baseURL).toBeUndefined()
|
||||
expect(openai.options.body.baseURL).toBeUndefined()
|
||||
expect(openai.endpoint).toEqual({ type: "aisdk", package: "test-provider" })
|
||||
}),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ describe("AzurePlugin", () => {
|
|||
item.endpoint = { type: "aisdk", package: "@ai-sdk/azure" }
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.azure)).options.aisdk.provider.resourceName).toBe("from-env")
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.azure)).options.body.resourceName).toBe("from-env")
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -52,7 +52,7 @@ describe("AzurePlugin", () => {
|
|||
yield* transform((catalog) => {
|
||||
const azure = provider("azure", {
|
||||
endpoint: { type: "aisdk", package: "@ai-sdk/azure" },
|
||||
options: { headers: {}, body: {}, aisdk: { provider: { resourceName: "from-config" }, request: {} } },
|
||||
options: { headers: {}, body: { resourceName: "from-config" }, },
|
||||
})
|
||||
catalog.provider.update(azure.id, (item) => {
|
||||
item.endpoint = azure.endpoint
|
||||
|
|
@ -60,10 +60,10 @@ describe("AzurePlugin", () => {
|
|||
})
|
||||
catalog.provider.update(ProviderV2.ID.openai, () => {})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.azure)).options.aisdk.provider.resourceName).toBe(
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.azure)).options.body.resourceName).toBe(
|
||||
"from-config",
|
||||
)
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.openai)).options.aisdk.provider.resourceName).toBeUndefined()
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.openai)).options.body.resourceName).toBeUndefined()
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -103,7 +103,7 @@ describe("AzurePlugin", () => {
|
|||
item.endpoint = { type: "aisdk", package: "@ai-sdk/azure" }
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.azure)).options.aisdk.provider.resourceName).toBe(
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.azure)).options.body.resourceName).toBe(
|
||||
"from-account",
|
||||
)
|
||||
}),
|
||||
|
|
@ -120,14 +120,14 @@ describe("AzurePlugin", () => {
|
|||
yield* transform((catalog) => {
|
||||
const azure = provider("azure", {
|
||||
endpoint: { type: "aisdk", package: "@ai-sdk/azure" },
|
||||
options: { headers: {}, body: {}, aisdk: { provider: { resourceName: "" }, request: {} } },
|
||||
options: { headers: {}, body: { resourceName: "" }, },
|
||||
})
|
||||
catalog.provider.update(azure.id, (item) => {
|
||||
item.endpoint = azure.endpoint
|
||||
item.options = azure.options
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.azure)).options.aisdk.provider.resourceName).toBe("from-env")
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.azure)).options.body.resourceName).toBe("from-env")
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -142,14 +142,14 @@ describe("AzurePlugin", () => {
|
|||
yield* transform((catalog) => {
|
||||
const azure = provider("azure", {
|
||||
endpoint: { type: "aisdk", package: "@ai-sdk/azure" },
|
||||
options: { headers: {}, body: {}, aisdk: { provider: { resourceName: " " }, request: {} } },
|
||||
options: { headers: {}, body: { resourceName: " " }, },
|
||||
})
|
||||
catalog.provider.update(azure.id, (item) => {
|
||||
item.endpoint = azure.endpoint
|
||||
item.options = azure.options
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.azure)).options.aisdk.provider.resourceName).toBe("from-env")
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.azure)).options.body.resourceName).toBe("from-env")
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -227,7 +227,7 @@ describe("AzurePlugin", () => {
|
|||
"aisdk.language",
|
||||
{
|
||||
model: model("azure", "deployment", {
|
||||
options: { headers: {}, body: {}, aisdk: { provider: {}, request: { useCompletionUrls: true } } },
|
||||
options: { headers: {}, body: { useCompletionUrls: true }, },
|
||||
}),
|
||||
sdk: fakeSelectorSdk(calls),
|
||||
options: {},
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ describe("CloudflareWorkersAIPlugin", () => {
|
|||
yield* transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.make("cloudflare-workers-ai"), (provider) => {
|
||||
provider.endpoint = { type: "aisdk", package: "test-provider" }
|
||||
provider.options.aisdk.provider.accountId = "configured-acct"
|
||||
provider.options.body.accountId = "configured-acct"
|
||||
}),
|
||||
)
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.make("cloudflare-workers-ai"))).endpoint).toEqual({
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ describe("GitLabPlugin", () => {
|
|||
{
|
||||
model: model("gitlab", "claude"),
|
||||
package: "gitlab-ai-provider",
|
||||
options: provider.options.aisdk.provider,
|
||||
options: provider.options.body,
|
||||
},
|
||||
{},
|
||||
)
|
||||
|
|
@ -238,7 +238,7 @@ describe("GitLabPlugin", () => {
|
|||
{
|
||||
model: model("gitlab", "claude"),
|
||||
package: "gitlab-ai-provider",
|
||||
options: provider.options.aisdk.provider,
|
||||
options: provider.options.body,
|
||||
},
|
||||
{},
|
||||
)
|
||||
|
|
@ -258,8 +258,7 @@ describe("GitLabPlugin", () => {
|
|||
model: model("gitlab", "duo-workflow-custom", {
|
||||
options: {
|
||||
headers: {},
|
||||
body: {},
|
||||
aisdk: { provider: {}, request: { workflowRef: "ref", workflowDefinition: "definition" } },
|
||||
body: { workflowRef: "ref", workflowDefinition: "definition" },
|
||||
},
|
||||
}),
|
||||
sdk: {
|
||||
|
|
@ -322,8 +321,7 @@ describe("GitLabPlugin", () => {
|
|||
model: model("gitlab", "duo-workflow-custom", {
|
||||
options: {
|
||||
headers: {},
|
||||
body: {},
|
||||
aisdk: { provider: {}, request: { featureFlags: { request_flag: true } } },
|
||||
body: { featureFlags: { request_flag: true } },
|
||||
},
|
||||
}),
|
||||
sdk: {
|
||||
|
|
@ -350,7 +348,7 @@ describe("GitLabPlugin", () => {
|
|||
"aisdk.language",
|
||||
{
|
||||
model: model("gitlab", "claude", {
|
||||
options: { headers: { h: "v" }, body: {}, aisdk: { provider: {}, request: {} } },
|
||||
options: { headers: { h: "v" }, body: {}, },
|
||||
}),
|
||||
sdk: {
|
||||
workflowChat: () => undefined,
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||
}),
|
||||
)
|
||||
const provider = yield* catalog.provider.get(ProviderV2.ID.make("google-vertex-anthropic"))
|
||||
expect(provider.options.aisdk.provider.project).toBe("cloud-project")
|
||||
expect(provider.options.aisdk.provider.location).toBe("cloud-location")
|
||||
expect(provider.options.body.project).toBe("cloud-project")
|
||||
expect(provider.options.body.location).toBe("cloud-location")
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
@ -45,13 +45,13 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||
yield* transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.make("google-vertex-anthropic"), (provider) => {
|
||||
provider.endpoint = { type: "aisdk", package: "@ai-sdk/google-vertex/anthropic" }
|
||||
provider.options.aisdk.provider.project = "configured-project"
|
||||
provider.options.aisdk.provider.location = "configured-location"
|
||||
provider.options.body.project = "configured-project"
|
||||
provider.options.body.location = "configured-location"
|
||||
}),
|
||||
)
|
||||
const provider = yield* catalog.provider.get(ProviderV2.ID.make("google-vertex-anthropic"))
|
||||
expect(provider.options.aisdk.provider.project).toBe("configured-project")
|
||||
expect(provider.options.aisdk.provider.location).toBe("configured-location")
|
||||
expect(provider.options.body.project).toBe("configured-project")
|
||||
expect(provider.options.body.location).toBe("configured-location")
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -61,8 +61,8 @@ describe("GoogleVertexPlugin", () => {
|
|||
}),
|
||||
)
|
||||
const provider = yield* catalog.provider.get(ProviderV2.ID.make("google-vertex"))
|
||||
expect(provider.options.aisdk.provider.project).toBe("google-cloud-project")
|
||||
expect(provider.options.aisdk.provider.location).toBe("google-vertex-location")
|
||||
expect(provider.options.body.project).toBe("google-cloud-project")
|
||||
expect(provider.options.body.location).toBe("google-vertex-location")
|
||||
expect(provider.endpoint).toEqual({
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
|
|
@ -112,7 +112,7 @@ describe("GoogleVertexPlugin", () => {
|
|||
{},
|
||||
)
|
||||
|
||||
expect(provider.options.aisdk.provider.project).toBe("vertex-project")
|
||||
expect(provider.options.body.project).toBe("vertex-project")
|
||||
expect(provider.endpoint).toEqual({
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
|
|
@ -147,13 +147,13 @@ describe("GoogleVertexPlugin", () => {
|
|||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}",
|
||||
}
|
||||
provider.options.aisdk.provider.project = "config-project"
|
||||
provider.options.aisdk.provider.location = "global"
|
||||
provider.options.body.project = "config-project"
|
||||
provider.options.body.location = "global"
|
||||
}),
|
||||
)
|
||||
const provider = yield* catalog.provider.get(ProviderV2.ID.make("google-vertex"))
|
||||
expect(provider.options.aisdk.provider.project).toBe("config-project")
|
||||
expect(provider.options.aisdk.provider.location).toBe("global")
|
||||
expect(provider.options.body.project).toBe("config-project")
|
||||
expect(provider.options.body.location).toBe("global")
|
||||
expect(provider.endpoint).toEqual({
|
||||
type: "aisdk",
|
||||
package: "@ai-sdk/openai-compatible",
|
||||
|
|
@ -176,8 +176,8 @@ describe("GoogleVertexPlugin", () => {
|
|||
package: "@ai-sdk/openai-compatible",
|
||||
url: "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}",
|
||||
}
|
||||
provider.options.aisdk.provider.project = "config-project"
|
||||
provider.options.aisdk.provider.location = "eu"
|
||||
provider.options.body.project = "config-project"
|
||||
provider.options.body.location = "eu"
|
||||
}),
|
||||
)
|
||||
const provider = yield* catalog.provider.get(ProviderV2.ID.make("google-vertex"))
|
||||
|
|
@ -208,12 +208,12 @@ describe("GoogleVertexPlugin", () => {
|
|||
yield* transform((catalog) =>
|
||||
catalog.provider.update(ProviderV2.ID.make("google-vertex"), (provider) => {
|
||||
provider.endpoint = { type: "aisdk", package: "@ai-sdk/google-vertex" }
|
||||
provider.options.aisdk.provider.project = "config-project"
|
||||
provider.options.body.project = "config-project"
|
||||
}),
|
||||
)
|
||||
const provider = yield* catalog.provider.get(ProviderV2.ID.make("google-vertex"))
|
||||
expect(provider.options.aisdk.provider.project).toBe("config-project")
|
||||
expect(provider.options.aisdk.provider.location).toBe("us-central1")
|
||||
expect(provider.options.body.project).toBe("config-project")
|
||||
expect(provider.options.body.location).toBe("us-central1")
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -58,11 +58,7 @@ describe("GooglePlugin", () => {
|
|||
},
|
||||
options: {
|
||||
headers: {},
|
||||
body: {},
|
||||
aisdk: {
|
||||
provider: { apiKey: "test" },
|
||||
request: {},
|
||||
},
|
||||
body: { apiKey: "test" },
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -89,11 +89,7 @@ describe("GroqPlugin", () => {
|
|||
},
|
||||
options: {
|
||||
headers: {},
|
||||
body: {},
|
||||
aisdk: {
|
||||
provider: { apiKey: "test" },
|
||||
request: {},
|
||||
},
|
||||
body: { apiKey: "test" },
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -65,10 +65,6 @@ export function provider(providerID: string, options?: Partial<ProviderV2.Info>)
|
|||
options: {
|
||||
headers: {},
|
||||
body: {},
|
||||
aisdk: {
|
||||
provider: {},
|
||||
request: {},
|
||||
},
|
||||
...options?.options,
|
||||
},
|
||||
})
|
||||
|
|
@ -86,10 +82,6 @@ export function model(providerID: string, modelID: string, options?: Partial<Mod
|
|||
options: {
|
||||
headers: {},
|
||||
body: {},
|
||||
aisdk: {
|
||||
provider: {},
|
||||
request: {},
|
||||
},
|
||||
...options?.options,
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ describe("KiloPlugin", () => {
|
|||
yield* transform((catalog) => {
|
||||
const kilo = provider("kilo", {
|
||||
endpoint: { type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://api.kilo.ai/api/gateway" },
|
||||
options: { headers: { Existing: "value" }, body: {}, aisdk: { provider: {}, request: {} } },
|
||||
options: { headers: { Existing: "value" }, body: {}, },
|
||||
})
|
||||
catalog.provider.update(kilo.id, (draft) => {
|
||||
draft.endpoint = kilo.endpoint
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ describe("LLMGatewayPlugin", () => {
|
|||
const llmgateway = provider("llmgateway", {
|
||||
enabled: { via: "env", name: "LLMGATEWAY_API_KEY" },
|
||||
endpoint: { type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://api.llmgateway.io/v1" },
|
||||
options: { headers: { Existing: "value" }, body: {}, aisdk: { provider: {}, request: {} } },
|
||||
options: { headers: { Existing: "value" }, body: {}, },
|
||||
})
|
||||
catalog.provider.update(llmgateway.id, (draft) => {
|
||||
draft.enabled = llmgateway.enabled
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ describe("NvidiaPlugin", () => {
|
|||
yield* transform((catalog) => {
|
||||
const nvidia = provider("nvidia", {
|
||||
endpoint: { type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://integrate.api.nvidia.com/v1" },
|
||||
options: { headers: { Existing: "value" }, body: {}, aisdk: { provider: {}, request: {} } },
|
||||
options: { headers: { Existing: "value" }, body: {}, },
|
||||
})
|
||||
catalog.provider.update(nvidia.id, (draft) => {
|
||||
draft.endpoint = nvidia.endpoint
|
||||
|
|
@ -53,7 +53,7 @@ describe("NvidiaPlugin", () => {
|
|||
yield* transform((catalog) => {
|
||||
const item = provider("nvidia", {
|
||||
endpoint: { type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://integrate.api.nvidia.com/v1" },
|
||||
options: { headers: {}, body: {}, aisdk: { provider: {}, request: {} } },
|
||||
options: { headers: {}, body: {}, },
|
||||
})
|
||||
catalog.provider.update(item.id, (draft) => {
|
||||
draft.endpoint = item.endpoint
|
||||
|
|
@ -80,8 +80,7 @@ describe("NvidiaPlugin", () => {
|
|||
endpoint: { type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://integrate.api.nvidia.com/v1" },
|
||||
options: {
|
||||
headers: { "X-BILLING-INVOKE-ORIGIN": "CustomOrigin" },
|
||||
body: {},
|
||||
aisdk: { provider: { baseURL: "https://integrate.api.nvidia.com/v1" }, request: {} },
|
||||
body: { baseURL: "https://integrate.api.nvidia.com/v1" },
|
||||
},
|
||||
})
|
||||
catalog.provider.update(item.id, (draft) => {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ describe("OpencodePlugin", () => {
|
|||
draft.cost = [...paid.cost]
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).options.aisdk.provider.apiKey).toBe("public")
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).options.body.apiKey).toBe("public")
|
||||
expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(false)
|
||||
}),
|
||||
),
|
||||
|
|
@ -54,7 +54,7 @@ describe("OpencodePlugin", () => {
|
|||
draft.cost = [...free.cost]
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).options.aisdk.provider.apiKey).toBe("public")
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).options.body.apiKey).toBe("public")
|
||||
expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("free"))).enabled).toBe(true)
|
||||
}),
|
||||
),
|
||||
|
|
@ -75,7 +75,7 @@ describe("OpencodePlugin", () => {
|
|||
draft.cost = [...outputOnly.cost]
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).options.aisdk.provider.apiKey).toBe("public")
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).options.body.apiKey).toBe("public")
|
||||
expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("output-only"))).enabled).toBe(true)
|
||||
}),
|
||||
),
|
||||
|
|
@ -96,7 +96,7 @@ describe("OpencodePlugin", () => {
|
|||
draft.cost = [...paid.cost]
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).options.aisdk.provider.apiKey).toBeUndefined()
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).options.body.apiKey).toBeUndefined()
|
||||
expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
||||
}),
|
||||
),
|
||||
|
|
@ -119,7 +119,7 @@ describe("OpencodePlugin", () => {
|
|||
draft.cost = [...paid.cost]
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).options.aisdk.provider.apiKey).toBeUndefined()
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).options.body.apiKey).toBeUndefined()
|
||||
expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
||||
}),
|
||||
),
|
||||
|
|
@ -136,11 +136,7 @@ describe("OpencodePlugin", () => {
|
|||
const item = provider("opencode", {
|
||||
options: {
|
||||
headers: {},
|
||||
body: {},
|
||||
aisdk: {
|
||||
provider: { apiKey: "configured" },
|
||||
request: {},
|
||||
},
|
||||
body: { apiKey: "configured" },
|
||||
},
|
||||
})
|
||||
catalog.provider.update(item.id, (draft) => {
|
||||
|
|
@ -151,7 +147,7 @@ describe("OpencodePlugin", () => {
|
|||
draft.cost = [...paid.cost]
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).options.aisdk.provider.apiKey).toBe("configured")
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).options.body.apiKey).toBe("configured")
|
||||
expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
||||
}),
|
||||
),
|
||||
|
|
@ -174,7 +170,7 @@ describe("OpencodePlugin", () => {
|
|||
draft.cost = [...paid.cost]
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).options.aisdk.provider.apiKey).toBeUndefined()
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.opencode)).options.body.apiKey).toBeUndefined()
|
||||
expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
||||
}),
|
||||
),
|
||||
|
|
@ -195,7 +191,7 @@ describe("OpencodePlugin", () => {
|
|||
draft.cost = [...paid.cost]
|
||||
})
|
||||
})
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.openai)).options.aisdk.provider.apiKey).toBeUndefined()
|
||||
expect((yield* catalog.provider.get(ProviderV2.ID.openai)).options.body.apiKey).toBeUndefined()
|
||||
expect((yield* catalog.model.get(ProviderV2.ID.openai, ModelV2.ID.make("paid"))).enabled).toBe(true)
|
||||
}),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ describe("OpenRouterPlugin", () => {
|
|||
yield* transform((catalog) => {
|
||||
const openrouter = provider("openrouter", {
|
||||
endpoint: { type: "aisdk", package: "@openrouter/ai-sdk-provider" },
|
||||
options: { headers: { Existing: "value" }, body: {}, aisdk: { provider: {}, request: {} } },
|
||||
options: { headers: { Existing: "value" }, body: {}, },
|
||||
})
|
||||
catalog.provider.update(openrouter.id, (item) => {
|
||||
item.endpoint = openrouter.endpoint
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ describe("VercelPlugin", () => {
|
|||
yield* transform((catalog) => {
|
||||
const item = provider("vercel", {
|
||||
endpoint: { type: "aisdk", package: "@ai-sdk/vercel" },
|
||||
options: { headers: { Existing: "1" }, body: {}, aisdk: { provider: {}, request: {} } },
|
||||
options: { headers: { Existing: "1" }, body: {}, },
|
||||
})
|
||||
catalog.provider.update(item.id, (draft) => {
|
||||
draft.endpoint = item.endpoint
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ describe("ZenmuxPlugin", () => {
|
|||
yield* transform((catalog) => {
|
||||
const item = provider("zenmux", {
|
||||
endpoint: { type: "aisdk", package: "@ai-sdk/openai-compatible", url: "https://zenmux.ai/api/v1" },
|
||||
options: { headers: { Existing: "value" }, body: {}, aisdk: { provider: {}, request: {} } },
|
||||
options: { headers: { Existing: "value" }, body: {}, },
|
||||
})
|
||||
catalog.provider.update(item.id, (draft) => {
|
||||
draft.endpoint = item.endpoint
|
||||
|
|
@ -74,7 +74,6 @@ describe("ZenmuxPlugin", () => {
|
|||
options: {
|
||||
headers: { "HTTP-Referer": "https://example.com/", "X-Title": "custom-title" },
|
||||
body: {},
|
||||
aisdk: { provider: {}, request: {} },
|
||||
},
|
||||
})
|
||||
catalog.provider.update(item.id, (draft) => {
|
||||
|
|
@ -101,7 +100,6 @@ describe("ZenmuxPlugin", () => {
|
|||
options: {
|
||||
headers: { "HTTP-Referer": "https://example.com/", "X-Title": "custom-title" },
|
||||
body: {},
|
||||
aisdk: { provider: {}, request: {} },
|
||||
},
|
||||
})
|
||||
catalog.provider.update(item.id, (draft) => {
|
||||
|
|
|
|||
|
|
@ -2730,14 +2730,6 @@ export type ModelV2Info = {
|
|||
body: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
aisdk: {
|
||||
provider: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
request: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
}
|
||||
variant?: string
|
||||
}
|
||||
variants: Array<{
|
||||
|
|
@ -2748,14 +2740,6 @@ export type ModelV2Info = {
|
|||
body: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
aisdk: {
|
||||
provider: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
request: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
}
|
||||
}>
|
||||
time: {
|
||||
released: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN"
|
||||
|
|
@ -3676,14 +3660,6 @@ export type ProviderV2Info = {
|
|||
body: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
aisdk: {
|
||||
provider: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
request: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3789,14 +3765,6 @@ export type ModelV2Info1 = {
|
|||
body: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
aisdk: {
|
||||
provider: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
request: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
}
|
||||
variant?: string
|
||||
}
|
||||
variants: Array<{
|
||||
|
|
@ -3807,14 +3775,6 @@ export type ModelV2Info1 = {
|
|||
body: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
aisdk: {
|
||||
provider: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
request: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
}
|
||||
}>
|
||||
time: {
|
||||
released: number | "NaN" | "Infinity" | "-Infinity"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue