feat: bump bedrock and add proper mantle support for openai models through aws bedrock (#30464)

This commit is contained in:
Aiden Cline
2026-06-04 00:09:12 -05:00
committed by GitHub
parent 2a33addd29
commit 9f42bd4a85
12 changed files with 305 additions and 92 deletions

View File

@@ -100,10 +100,13 @@ function googleVertexAnthropicBaseURL(project: string | undefined, location: str
type BundledSDK = {
languageModel(modelId: string): LanguageModelV3
chat?: (modelId: string) => LanguageModelV3
responses?: (modelId: string) => LanguageModelV3
}
const BUNDLED_PROVIDERS: Record<string, () => Promise<(opts: any) => BundledSDK>> = {
"@ai-sdk/amazon-bedrock": () => import("@ai-sdk/amazon-bedrock").then((m) => m.createAmazonBedrock),
"@ai-sdk/amazon-bedrock/mantle": () => import("@ai-sdk/amazon-bedrock/mantle").then((m) => m.createBedrockMantle),
"@ai-sdk/anthropic": () => import("@ai-sdk/anthropic").then((m) => m.createAnthropic),
"@ai-sdk/azure": () => import("@ai-sdk/azure").then((m) => m.createAzure),
"@ai-sdk/google": () => import("@ai-sdk/google").then((m) => m.createGoogleGenerativeAI),
@@ -130,7 +133,7 @@ const BUNDLED_PROVIDERS: Record<string, () => Promise<(opts: any) => BundledSDK>
"venice-ai-sdk-provider": () => import("venice-ai-sdk-provider").then((m) => m.createVenice),
}
type CustomModelLoader = (sdk: any, modelID: string, options?: Record<string, any>) => Promise<any>
type CustomModelLoader = (sdk: any, modelID: string, options?: Record<string, any>, model?: Model) => Promise<any>
type CustomVarsLoader = (options: Record<string, any>) => Record<string, string>
type CustomDiscoverModels = () => Promise<Record<string, Model>>
type CustomLoader = (provider: Info) => Effect.Effect<{
@@ -156,6 +159,12 @@ function selectAzureLanguageModel(sdk: any, modelID: string, useChat: boolean) {
return sdk.languageModel(modelID)
}
function selectBedrockMantleLanguageModel(sdk: BundledSDK, modelID: string) {
if (modelID === "openai.gpt-oss-safeguard-20b" || modelID === "openai.gpt-oss-safeguard-120b")
return sdk.chat?.(modelID) ?? sdk.languageModel(modelID)
return sdk.responses?.(modelID) ?? sdk.languageModel(modelID)
}
function custom(dep: CustomDep): Record<string, CustomLoader> {
return {
anthropic: () =>
@@ -331,7 +340,9 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
return {
autoload: true,
options: providerOptions,
async getModel(sdk: any, modelID: string, options?: Record<string, any>) {
async getModel(sdk: any, modelID: string, options?: Record<string, any>, model?: Model) {
if (model?.api.npm === "@ai-sdk/amazon-bedrock/mantle") return selectBedrockMantleLanguageModel(sdk, modelID)
// Skip region prefixing if model already has a cross-region inference profile prefix
// Models from models.dev may already include prefixes like us., eu., global., etc.
const crossRegionPrefixes = ["global.", "us.", "eu.", "jp.", "apac.", "au."]
@@ -1618,7 +1629,9 @@ export const layer = Layer.effect(
// Strip openai itemId metadata following what codex does
if (
(model.api.npm === "@ai-sdk/openai" || model.api.npm === "@ai-sdk/azure") &&
(model.api.npm === "@ai-sdk/openai" ||
model.api.npm === "@ai-sdk/azure" ||
model.api.npm === "@ai-sdk/amazon-bedrock/mantle") &&
opts.body &&
opts.method === "POST"
) {
@@ -1725,10 +1738,15 @@ export const layer = Layer.effect(
async () => {
const sdk = await resolveSDK(model, s, envs)
const language = s.modelLoaders[model.providerID]
? await s.modelLoaders[model.providerID](sdk, model.api.id, {
...provider.options,
...model.options,
})
? await s.modelLoaders[model.providerID](
sdk,
model.api.id,
{
...provider.options,
...model.options,
},
model,
)
: sdk.languageModel(model.api.id)
s.models.set(key, language)
return language

View File

@@ -35,6 +35,8 @@ function sdkKey(npm: string): string | undefined {
return "azure"
case "@ai-sdk/openai":
return "openai"
case "@ai-sdk/amazon-bedrock/mantle":
return "openai"
case "@ai-sdk/amazon-bedrock":
return "bedrock"
case "@ai-sdk/anthropic":
@@ -815,6 +817,7 @@ export function variants(model: Provider.Model): Record<string, Record<string, a
},
]),
)
case "@ai-sdk/amazon-bedrock/mantle":
case "@ai-sdk/openai": {
// https://v5.ai-sdk.dev/providers/ai-sdk-providers/openai
const efforts = openaiReasoningEfforts(model.api.id, model.release_date)
@@ -1021,7 +1024,8 @@ export function options(input: {
if (
input.model.providerID === "openai" ||
input.model.api.npm === "@ai-sdk/openai" ||
input.model.api.npm === "@ai-sdk/github-copilot"
input.model.api.npm === "@ai-sdk/github-copilot" ||
input.model.api.npm === "@ai-sdk/amazon-bedrock/mantle"
) {
result["store"] = false
}
@@ -1107,7 +1111,7 @@ export function options(input: {
if (!input.model.api.id.includes("gpt-5-pro")) {
result["reasoningEffort"] = "medium"
result["reasoningSummary"] = "auto"
if (input.model.api.npm === "@ai-sdk/openai") {
if (input.model.api.npm === "@ai-sdk/openai" || input.model.api.npm === "@ai-sdk/amazon-bedrock/mantle") {
result["include"] = INCLUDE_ENCRYPTED_REASONING
}
}

View File

@@ -159,6 +159,7 @@ export const toModelMessagesEffect = Effect.fnUntraced(function* (
const supportsMediaInToolResult = (attachment: { mime: string }) => {
if (model.api.npm === "@ai-sdk/anthropic") return true
if (model.api.npm === "@ai-sdk/openai") return true
if (model.api.npm === "@ai-sdk/amazon-bedrock/mantle") return true
if (model.api.npm === "@ai-sdk/amazon-bedrock") return attachment.mime.startsWith("image/")
if (model.api.npm === "@ai-sdk/xai") return attachment.mime.startsWith("image/")
if (model.api.npm === "@ai-sdk/google-vertex/anthropic") return true