feat(ai): add native Bedrock Mantle support (#40119)

Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com>
This commit is contained in:
opencode-agent[bot] 2026-08-02 11:16:22 -05:00 committed by GitHub
commit bf7f0cb521
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 506 additions and 44 deletions

View file

@ -10,32 +10,103 @@ export interface Mapping {
readonly body?: Readonly<Record<string, unknown>>
}
export function map(packageName: string | undefined, settings: Readonly<Record<string, unknown>>): Mapping | undefined {
const baseSettings = mapBaseSettings(settings)
switch (packageName) {
export interface MapInput {
readonly packageName: string | undefined
readonly settings: Readonly<Record<string, unknown>>
readonly modelID: string
readonly hasCredential?: boolean
}
export function map(input: MapInput): Mapping | undefined {
const baseSettings = mapBaseSettings(input.settings)
switch (input.packageName) {
case "@ai-sdk/amazon-bedrock/mantle":
return mapBedrockMantle(input, baseSettings)
case "@ai-sdk/google":
return {
package: "@opencode-ai/ai/providers/google",
settings: {
...baseSettings,
...mapAPIKey(settings),
...mapGoogleOptions(settings),
...mapAPIKey(input.settings),
...mapGoogleOptions(input.settings),
},
}
case "@openrouter/ai-sdk-provider":
return mapOpenRouter(settings, baseSettings)
return mapOpenRouter(input.settings, baseSettings)
case "@ai-sdk/xai":
return {
package: "@opencode-ai/ai/providers/xai",
settings: {
...baseSettings,
...mapAPIKey(settings),
...mapXAIOptions(settings),
...mapAPIKey(input.settings),
...mapXAIOptions(input.settings),
},
}
}
}
function mapBedrockMantle(input: MapInput, baseSettings: Readonly<Record<string, unknown>>): Mapping | undefined {
const settings = input.settings
const apiKey =
typeof settings.apiKey === "string"
? settings.apiKey
: typeof settings.bearerToken === "string"
? settings.bearerToken
: undefined
const credentials = mapBedrockCredentials(settings)
if (!input.hasCredential && apiKey === undefined && credentials === undefined) return undefined
const chat = input.modelID === "openai.gpt-oss-safeguard-20b" || input.modelID === "openai.gpt-oss-safeguard-120b"
return {
package: `@opencode-ai/ai/providers/amazon-bedrock/mantle/${chat ? "chat" : "responses"}`,
settings: {
...baseSettings,
...(typeof settings.baseURL !== "string" && typeof settings.endpoint === "string"
? { baseURL: settings.endpoint }
: {}),
...(apiKey === undefined ? {} : { apiKey }),
...(credentials === undefined ? {} : { credentials }),
...(typeof settings.region === "string" ? { region: settings.region } : {}),
...mapOpenAIOptions(settings),
},
}
}
function mapBedrockCredentials(settings: Readonly<Record<string, unknown>>) {
const credentials = isRecord(settings.credentials) ? settings.credentials : settings
const region =
typeof settings.region === "string"
? settings.region
: typeof credentials.region === "string"
? credentials.region
: undefined
if (
region === undefined ||
typeof credentials.accessKeyId !== "string" ||
typeof credentials.secretAccessKey !== "string"
)
return undefined
return {
region,
accessKeyId: credentials.accessKeyId,
secretAccessKey: credentials.secretAccessKey,
...(typeof credentials.sessionToken === "string" ? { sessionToken: credentials.sessionToken } : {}),
}
}
function mapOpenAIOptions(settings: Readonly<Record<string, unknown>>) {
const options = {
...(typeof settings.reasoningEffort === "string" ? { reasoningEffort: settings.reasoningEffort } : {}),
...(typeof settings.reasoningSummary === "string" ? { reasoningSummary: settings.reasoningSummary } : {}),
...(Array.isArray(settings.include) ? { include: settings.include } : {}),
...(typeof settings.store === "boolean" ? { store: settings.store } : {}),
...(typeof settings.promptCacheKey === "string" ? { promptCacheKey: settings.promptCacheKey } : {}),
...(typeof settings.textVerbosity === "string" ? { textVerbosity: settings.textVerbosity } : {}),
...(typeof settings.serviceTier === "string" ? { serviceTier: settings.serviceTier } : {}),
}
if (Object.keys(options).length === 0) return {}
return { providerOptions: { openai: options } }
}
function mapBaseSettings(settings: Readonly<Record<string, unknown>>) {
return {
...(typeof settings.baseURL === "string" ? { baseURL: settings.baseURL } : {}),

View file

@ -176,14 +176,20 @@ export const fromCatalogModel = (
)
}
const configured = { ...resolved.settings, ...credential?.metadata }
const mapping = Provider.isAISDK(resolved.package) ? AISDKNative.map(packageName, configured) : undefined
const mapping = Provider.isAISDK(resolved.package)
? AISDKNative.map({
packageName,
settings: configured,
modelID: resolved.modelID ?? resolved.id,
hasCredential: key !== undefined,
})
: undefined
const native = mapping?.package ?? resolved.package
if (Provider.isAISDK(resolved.package) && !mapping) {
if (!dependencies?.loadAISDK) return Effect.fail(unsupported(resolved))
const runtime = produce(resolved, (draft) => {
draft.settings = Provider.mergeOverlay(draft.settings, {
...(credential?.type === "key" ? { apiKey: credential.key } : {}),
...(credential?.type === "oauth" ? { apiKey: credential.access } : {}),
...nativeCredentialSettings(resolved.package ?? "", credential),
...credential?.metadata,
})
})

View file

@ -34,6 +34,15 @@ export type ProviderPackage = ProviderPackageDefinition
const packages = new Map<string, Promise<unknown>>()
const builtins = new Map<string, () => Promise<unknown>>([
["@opencode-ai/ai/providers/amazon-bedrock", () => import("@opencode-ai/ai/providers/amazon-bedrock")],
["@opencode-ai/ai/providers/amazon-bedrock/mantle", () => import("@opencode-ai/ai/providers/amazon-bedrock/mantle")],
[
"@opencode-ai/ai/providers/amazon-bedrock/mantle/chat",
() => import("@opencode-ai/ai/providers/amazon-bedrock/mantle/chat"),
],
[
"@opencode-ai/ai/providers/amazon-bedrock/mantle/responses",
() => import("@opencode-ai/ai/providers/amazon-bedrock/mantle/responses"),
],
["@opencode-ai/ai/providers/anthropic", () => import("@opencode-ai/ai/providers/anthropic")],
["@opencode-ai/ai/providers/azure", () => import("@opencode-ai/ai/providers/azure")],
["@opencode-ai/ai/providers/azure/chat", () => import("@opencode-ai/ai/providers/azure/chat")],