refactor(core): isolate AI SDK native mappings (#39761)
This commit is contained in:
parent
7814568ba0
commit
cc1289048e
3 changed files with 67 additions and 26 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
import type { Model, ProviderOptions } from "./schema"
|
import type { Model, ProviderOptions } from "./schema"
|
||||||
|
|
||||||
export interface Settings extends Readonly<Record<string, unknown>> {
|
export interface Settings extends Readonly<Record<string, unknown>> {
|
||||||
|
readonly baseURL?: string
|
||||||
readonly headers?: Readonly<Record<string, string>>
|
readonly headers?: Readonly<Record<string, string>>
|
||||||
readonly body?: Readonly<Record<string, unknown>>
|
readonly body?: Readonly<Record<string, unknown>>
|
||||||
readonly limits?: {
|
readonly limits?: {
|
||||||
|
|
|
||||||
59
packages/core/src/aisdk-native.ts
Normal file
59
packages/core/src/aisdk-native.ts
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
export * as AISDKNative from "./aisdk-native"
|
||||||
|
|
||||||
|
export interface Mapping {
|
||||||
|
readonly package: string
|
||||||
|
readonly settings: Readonly<Record<string, unknown>>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function map(packageName: string | undefined, settings: Readonly<Record<string, unknown>>): Mapping | undefined {
|
||||||
|
const baseSettings = mapBaseSettings(settings)
|
||||||
|
switch (packageName) {
|
||||||
|
case "@ai-sdk/google":
|
||||||
|
return {
|
||||||
|
package: "@opencode-ai/ai/providers/google",
|
||||||
|
settings: {
|
||||||
|
...baseSettings,
|
||||||
|
...mapAPIKey(settings),
|
||||||
|
...mapProviderOptions("gemini", settings),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case "@openrouter/ai-sdk-provider":
|
||||||
|
return {
|
||||||
|
package: "@opencode-ai/ai/providers/openrouter",
|
||||||
|
settings: {
|
||||||
|
...baseSettings,
|
||||||
|
...mapAPIKey(settings),
|
||||||
|
...mapProviderOptions("openrouter", settings),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case "@ai-sdk/xai":
|
||||||
|
return {
|
||||||
|
package: "@opencode-ai/ai/providers/xai",
|
||||||
|
settings: {
|
||||||
|
...baseSettings,
|
||||||
|
...mapAPIKey(settings),
|
||||||
|
...mapProviderOptions("xai", settings),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapBaseSettings(settings: Readonly<Record<string, unknown>>) {
|
||||||
|
return {
|
||||||
|
...(typeof settings.baseURL === "string" ? { baseURL: settings.baseURL } : {}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapAPIKey(settings: Readonly<Record<string, unknown>>) {
|
||||||
|
return typeof settings.apiKey === "string" ? { apiKey: settings.apiKey } : {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapProviderOptions(namespace: string, settings: Readonly<Record<string, unknown>>) {
|
||||||
|
const values = Object.fromEntries(
|
||||||
|
Object.entries(settings).filter(
|
||||||
|
([key]) => !["apiKey", "authToken", "baseURL", "chunkTimeout", "fetch", "timeout"].includes(key),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if (Object.keys(values).length === 0) return {}
|
||||||
|
return { providerOptions: { [namespace]: values } }
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,7 @@ import { Auth, type AnyRoute } from "@opencode-ai/ai/route"
|
||||||
import { Context, Effect, Layer, Schema } from "effect"
|
import { Context, Effect, Layer, Schema } from "effect"
|
||||||
import { produce } from "immer"
|
import { produce } from "immer"
|
||||||
import { AISDK } from "./aisdk"
|
import { AISDK } from "./aisdk"
|
||||||
|
import { AISDKNative } from "./aisdk-native"
|
||||||
import { Catalog } from "./catalog"
|
import { Catalog } from "./catalog"
|
||||||
import { Credential } from "./credential"
|
import { Credential } from "./credential"
|
||||||
import { Integration } from "./integration"
|
import { Integration } from "./integration"
|
||||||
|
|
@ -182,8 +183,10 @@ export const fromCatalogModel = (
|
||||||
.model({ id: resolved.modelID ?? resolved.id, compatibility: resolved.compatibility }),
|
.model({ id: resolved.modelID ?? resolved.id, compatibility: resolved.compatibility }),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const native = Provider.isAISDK(resolved.package) ? nativePackage(packageName) : resolved.package
|
const configured = { ...resolved.settings, ...credential?.metadata }
|
||||||
if (Provider.isAISDK(resolved.package) && !native) {
|
const mapping = Provider.isAISDK(resolved.package) ? AISDKNative.map(packageName, configured) : undefined
|
||||||
|
const native = mapping?.package ?? resolved.package
|
||||||
|
if (Provider.isAISDK(resolved.package) && !mapping) {
|
||||||
if (!dependencies?.loadAISDK) return Effect.fail(unsupported(resolved))
|
if (!dependencies?.loadAISDK) return Effect.fail(unsupported(resolved))
|
||||||
const runtime = produce(resolved, (draft) => {
|
const runtime = produce(resolved, (draft) => {
|
||||||
draft.settings = Provider.mergeOverlay(draft.settings, {
|
draft.settings = Provider.mergeOverlay(draft.settings, {
|
||||||
|
|
@ -201,15 +204,13 @@ export const fromCatalogModel = (
|
||||||
const module = yield* (dependencies?.loadPackage ?? Provider.loadPackage)(specifier).pipe(
|
const module = yield* (dependencies?.loadPackage ?? Provider.loadPackage)(specifier).pipe(
|
||||||
Effect.mapError(() => unsupported(resolved)),
|
Effect.mapError(() => unsupported(resolved)),
|
||||||
)
|
)
|
||||||
const configured = { ...resolved.settings, ...credential?.metadata }
|
const mapped = mapping?.settings ?? configured
|
||||||
const providerOptions = nativeProviderOptions(packageName, configured)
|
|
||||||
const settings = {
|
const settings = {
|
||||||
...(credential ? withoutNativeAuthSettings(configured) : configured),
|
...(credential ? withoutNativeAuthSettings(mapped) : mapped),
|
||||||
...nativeCredentialSettings(specifier, credential),
|
...nativeCredentialSettings(specifier, credential),
|
||||||
headers: resolved.headers,
|
headers: resolved.headers,
|
||||||
body: resolved.body,
|
body: resolved.body,
|
||||||
limits: { context: resolved.limit.context, output: resolved.limit.output },
|
limits: { context: resolved.limit.context, output: resolved.limit.output },
|
||||||
...(providerOptions ? { providerOptions } : {}),
|
|
||||||
}
|
}
|
||||||
return yield* Effect.try({
|
return yield* Effect.try({
|
||||||
try: () => {
|
try: () => {
|
||||||
|
|
@ -226,26 +227,6 @@ export const fromCatalogModel = (
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const nativePackage = (packageName: string | undefined) => {
|
|
||||||
if (packageName === "@ai-sdk/google") return "@opencode-ai/ai/providers/google"
|
|
||||||
if (packageName === "@openrouter/ai-sdk-provider") return "@opencode-ai/ai/providers/openrouter"
|
|
||||||
if (packageName === "@ai-sdk/xai") return "@opencode-ai/ai/providers/xai"
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
const nativeProviderOptions = (packageName: string | undefined, settings: Readonly<Record<string, unknown>>) => {
|
|
||||||
const values = Object.fromEntries(
|
|
||||||
Object.entries(settings).filter(
|
|
||||||
([key]) => !["apiKey", "authToken", "baseURL", "chunkTimeout", "fetch", "timeout"].includes(key),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if (Object.keys(values).length === 0) return undefined
|
|
||||||
if (packageName === "@ai-sdk/google") return { gemini: values }
|
|
||||||
if (packageName === "@openrouter/ai-sdk-provider") return { openrouter: values }
|
|
||||||
if (packageName === "@ai-sdk/xai") return { xai: values }
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
const isNativeOpenAI = (packageName: string | undefined) =>
|
const isNativeOpenAI = (packageName: string | undefined) =>
|
||||||
packageName === "@opencode-ai/ai/providers/openai" ||
|
packageName === "@opencode-ai/ai/providers/openai" ||
|
||||||
packageName?.startsWith("@opencode-ai/ai/providers/openai/") === true
|
packageName?.startsWith("@opencode-ai/ai/providers/openai/") === true
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue