| 1263 | } |
| 1264 | |
| 1265 | export function providerOptions(model: Provider.Model, options: { [x: string]: any }) { |
| 1266 | const usesOpenAIReasoningGate = |
| 1267 | model.api.npm === "@ai-sdk/openai" || |
| 1268 | model.api.npm === "@ai-sdk/azure" || |
| 1269 | model.api.npm === "@ai-sdk/amazon-bedrock/mantle" |
| 1270 | const normalized = |
| 1271 | usesOpenAIReasoningGate && |
| 1272 | (model.capabilities.reasoning || options.reasoningEffort !== undefined || options.reasoningSummary !== undefined) |
| 1273 | ? { ...options, forceReasoning: true } |
| 1274 | : options |
| 1275 | |
| 1276 | if (model.api.npm === "@ai-sdk/gateway") { |
| 1277 | // Gateway providerOptions are split across two namespaces: |
| 1278 | // - `gateway`: gateway-native routing/caching controls (order, only, byok, etc.) |
| 1279 | // - `<upstream slug>`: provider-specific model options (anthropic/openai/...) |
| 1280 | // We keep `gateway` as-is and route every other top-level option under the |
| 1281 | // model-derived upstream slug. |
| 1282 | const i = model.api.id.indexOf("/") |
| 1283 | const rawSlug = i > 0 ? model.api.id.slice(0, i) : undefined |
| 1284 | const slug = rawSlug ? (SLUG_OVERRIDES[rawSlug] ?? rawSlug) : undefined |
| 1285 | const gateway = normalized.gateway |
| 1286 | const rest = Object.fromEntries(Object.entries(normalized).filter(([k]) => k !== "gateway")) |
| 1287 | const has = Object.keys(rest).length > 0 |
| 1288 | |
| 1289 | const result: Record<string, any> = {} |
| 1290 | if (gateway !== undefined) result.gateway = gateway |
| 1291 | |
| 1292 | if (has) { |
| 1293 | if (slug) { |
| 1294 | // Route model-specific options under the provider slug |
| 1295 | result[slug] = rest |
| 1296 | } else if (gateway && typeof gateway === "object" && !Array.isArray(gateway)) { |
| 1297 | result.gateway = { ...gateway, ...rest } |
| 1298 | } else { |
| 1299 | result.gateway = rest |
| 1300 | } |
| 1301 | } |
| 1302 | |
| 1303 | return result |
| 1304 | } |
| 1305 | |
| 1306 | // AI SDK packages that resolve providerOptionsName by splitting the |
| 1307 | // provider name on "." (e.g. "wafer.ai" -> "wafer") need the same |
| 1308 | // logic here so the key we write matches the key they read. |
| 1309 | // Other SDKs (xai, mistral, groq, cohere, etc.) use hardcoded keys |
| 1310 | // like "xai" or "cohere" - applying .split(".")[0] would break those. |
| 1311 | const usesDotSplitOptions = |
| 1312 | model.api.npm === "@ai-sdk/openai-compatible" || |
| 1313 | model.api.npm === "@ai-sdk/openai" || |
| 1314 | model.api.npm === "@ai-sdk/anthropic" |
| 1315 | const key = sdkKey(model.api.npm) ?? (usesDotSplitOptions ? model.providerID.split(".")[0] : model.providerID) |
| 1316 | // @ai-sdk/azure delegates to OpenAIChatLanguageModel which reads from |
| 1317 | // providerOptions["openai"], but OpenAIResponsesLanguageModel checks |
| 1318 | // "azure" first. Pass both so model options work on either code path. |
| 1319 | if (model.api.npm === "@ai-sdk/azure") { |
| 1320 | return { openai: normalized, azure: normalized } |
| 1321 | } |
| 1322 | return { [key]: normalized } |