2025-11-13 22:39:09 +00:00
import { describe , expect , test } from "bun:test"
2026-06-05 15:08:39 +00:00
import { Effect } from "effect"
2026-04-27 18:33:33 +00:00
import { ProviderTransform } from "@/provider/transform"
2026-06-05 15:08:39 +00:00
import { LLMRequestPrep } from "@/session/llm/request"
2026-05-31 01:08:38 +00:00
import { ProviderV2 } from "@opencode-ai/core/provider"
2026-06-04 06:57:43 +00:00
import { ModelV2 } from "@opencode-ai/core/model"
2026-06-25 21:28:39 +00:00
import { jsonSchema } from "ai"
2025-11-13 22:39:09 +00:00
2025-12-17 16:24:53 +00:00
describe ( "ProviderTransform.options - setCacheKey" , ( ) = > {
const sessionID = "test-session-123"
const mockModel = {
id : "anthropic/claude-3-5-sonnet" ,
providerID : "anthropic" ,
api : {
id : "claude-3-5-sonnet-20241022" ,
url : "https://api.anthropic.com" ,
npm : "@ai-sdk/anthropic" ,
} ,
name : "Claude 3.5 Sonnet" ,
capabilities : {
temperature : true ,
reasoning : false ,
attachment : true ,
toolcall : true ,
input : { text : true , audio : false , image : true , video : false , pdf : true } ,
output : { text : true , audio : false , image : false , video : false , pdf : false } ,
interleaved : false ,
} ,
cost : {
input : 0.003 ,
output : 0.015 ,
cache : { read : 0.0003 , write : 0.00375 } ,
} ,
limit : {
context : 200000 ,
output : 8192 ,
} ,
status : "active" ,
options : { } ,
headers : { } ,
} as any
test ( "should set promptCacheKey when providerOptions.setCacheKey is true" , ( ) = > {
2026-01-15 07:31:50 +00:00
const result = ProviderTransform . options ( {
model : mockModel ,
sessionID ,
providerOptions : { setCacheKey : true } ,
} )
2025-12-17 16:24:53 +00:00
expect ( result . promptCacheKey ) . toBe ( sessionID )
} )
test ( "should not set promptCacheKey when providerOptions.setCacheKey is false" , ( ) = > {
2026-01-15 07:31:50 +00:00
const result = ProviderTransform . options ( {
model : mockModel ,
sessionID ,
providerOptions : { setCacheKey : false } ,
} )
2025-12-17 16:24:53 +00:00
expect ( result . promptCacheKey ) . toBeUndefined ( )
} )
test ( "should not set promptCacheKey when providerOptions is undefined" , ( ) = > {
2026-01-15 07:31:50 +00:00
const result = ProviderTransform . options ( {
model : mockModel ,
sessionID ,
providerOptions : undefined ,
} )
2025-12-17 16:24:53 +00:00
expect ( result . promptCacheKey ) . toBeUndefined ( )
} )
test ( "should not set promptCacheKey when providerOptions does not have setCacheKey" , ( ) = > {
2026-01-15 07:31:50 +00:00
const result = ProviderTransform . options ( { model : mockModel , sessionID , providerOptions : { } } )
2025-12-17 16:24:53 +00:00
expect ( result . promptCacheKey ) . toBeUndefined ( )
} )
test ( "should set promptCacheKey for openai provider regardless of setCacheKey" , ( ) = > {
const openaiModel = {
. . . mockModel ,
providerID : "openai" ,
api : {
id : "gpt-4" ,
url : "https://api.openai.com" ,
npm : "@ai-sdk/openai" ,
} ,
}
2026-01-15 07:31:50 +00:00
const result = ProviderTransform . options ( { model : openaiModel , sessionID , providerOptions : { } } )
2025-12-17 16:24:53 +00:00
expect ( result . promptCacheKey ) . toBe ( sessionID )
} )
2026-01-15 07:31:50 +00:00
test ( "should set store=false for openai provider" , ( ) = > {
const openaiModel = {
. . . mockModel ,
providerID : "openai" ,
api : {
id : "gpt-4" ,
url : "https://api.openai.com" ,
npm : "@ai-sdk/openai" ,
} ,
}
const result = ProviderTransform . options ( {
model : openaiModel ,
sessionID ,
providerOptions : { } ,
} )
expect ( result . store ) . toBe ( false )
} )
2026-04-16 17:19:01 +00:00
2026-04-29 21:49:28 +00:00
test ( "should set store=false for azure provider by default" , ( ) = > {
2026-04-16 17:19:01 +00:00
const azureModel = {
. . . mockModel ,
providerID : "azure" ,
api : {
id : "gpt-4" ,
url : "https://azure.com" ,
npm : "@ai-sdk/azure" ,
} ,
}
const result = ProviderTransform . options ( {
model : azureModel ,
sessionID ,
providerOptions : { } ,
} )
2026-04-29 21:49:28 +00:00
expect ( result . store ) . toBe ( false )
2026-04-16 17:19:01 +00:00
} )
2025-12-17 16:24:53 +00:00
} )
2026-04-11 20:51:49 +00:00
describe ( "ProviderTransform.options - zai/zhipuai thinking" , ( ) = > {
const sessionID = "test-session-123"
const createModel = ( providerID : string ) = >
( {
id : ` ${ providerID } /glm-4.6 ` ,
providerID ,
api : {
id : "glm-4.6" ,
url : "https://open.bigmodel.cn/api/paas/v4" ,
npm : "@ai-sdk/openai-compatible" ,
} ,
name : "GLM 4.6" ,
capabilities : {
temperature : true ,
reasoning : true ,
attachment : true ,
toolcall : true ,
input : { text : true , audio : false , image : true , video : false , pdf : true } ,
output : { text : true , audio : false , image : false , video : false , pdf : false } ,
interleaved : false ,
} ,
cost : {
input : 0.001 ,
output : 0.002 ,
cache : { read : 0.0001 , write : 0.0002 } ,
} ,
limit : {
context : 128000 ,
output : 8192 ,
} ,
status : "active" ,
options : { } ,
headers : { } ,
} ) as any
for ( const providerID of [ "zai-coding-plan" , "zai" , "zhipuai-coding-plan" , "zhipuai" ] ) {
test ( ` ${ providerID } should set thinking cfg ` , ( ) = > {
const result = ProviderTransform . options ( {
model : createModel ( providerID ) ,
sessionID ,
providerOptions : { } ,
} )
expect ( result . thinking ) . toEqual ( {
type : "enabled" ,
clear_thinking : false ,
} )
} )
}
} )
2026-06-08 22:03:31 +00:00
describe ( "ProviderTransform.options - minimax m3 thinking" , ( ) = > {
const createModel = ( npm : string ) = >
( {
id : "minimax/minimax-m3" ,
providerID : "minimax" ,
api : {
id : "minimax-m3" ,
url : "https://api.minimax.com" ,
npm ,
} ,
capabilities : { reasoning : true } ,
limit : { output : 64_000 } ,
} ) as any
test ( "explicitly enables adaptive thinking with the anthropic SDK" , ( ) = > {
expect (
ProviderTransform . options ( {
model : createModel ( "@ai-sdk/anthropic" ) ,
sessionID : "test-session-123" ,
} ) . thinking ,
) . toEqual ( { type : "adaptive" } )
} )
test ( "uses the native default with the openai-compatible SDK" , ( ) = > {
expect (
ProviderTransform . options ( {
model : createModel ( "@ai-sdk/openai-compatible" ) ,
sessionID : "test-session-123" ,
} ) . thinking ,
) . toBeUndefined ( )
} )
} )
2026-03-21 16:57:52 +00:00
describe ( "ProviderTransform.options - google thinkingConfig gating" , ( ) = > {
const sessionID = "test-session-123"
const createGoogleModel = ( reasoning : boolean , npm : "@ai-sdk/google" | "@ai-sdk/google-vertex" ) = >
( {
id : ` ${ npm === "@ai-sdk/google" ? "google" : "google-vertex" } /gemini-2.0-flash ` ,
providerID : npm === "@ai-sdk/google" ? "google" : "google-vertex" ,
api : {
id : "gemini-2.0-flash" ,
url : npm === "@ai-sdk/google" ? "https://generativelanguage.googleapis.com" : "https://vertexai.googleapis.com" ,
npm ,
} ,
name : "Gemini 2.0 Flash" ,
capabilities : {
temperature : true ,
reasoning ,
attachment : true ,
toolcall : true ,
input : { text : true , audio : false , image : true , video : false , pdf : true } ,
output : { text : true , audio : false , image : false , video : false , pdf : false } ,
interleaved : false ,
} ,
cost : {
input : 0.001 ,
output : 0.002 ,
cache : { read : 0.0001 , write : 0.0002 } ,
} ,
limit : {
context : 1_000_000 ,
output : 8192 ,
} ,
status : "active" ,
options : { } ,
headers : { } ,
} ) as any
test ( "does not set thinkingConfig for google models without reasoning capability" , ( ) = > {
const result = ProviderTransform . options ( {
model : createGoogleModel ( false , "@ai-sdk/google" ) ,
sessionID ,
providerOptions : { } ,
} )
expect ( result . thinkingConfig ) . toBeUndefined ( )
} )
test ( "sets thinkingConfig for google models with reasoning capability" , ( ) = > {
const result = ProviderTransform . options ( {
model : createGoogleModel ( true , "@ai-sdk/google" ) ,
sessionID ,
providerOptions : { } ,
} )
expect ( result . thinkingConfig ) . toEqual ( {
includeThoughts : true ,
} )
} )
test ( "does not set thinkingConfig for vertex models without reasoning capability" , ( ) = > {
const result = ProviderTransform . options ( {
model : createGoogleModel ( false , "@ai-sdk/google-vertex" ) ,
sessionID ,
providerOptions : { } ,
} )
expect ( result . thinkingConfig ) . toBeUndefined ( )
} )
} )
2026-01-30 20:57:01 +00:00
describe ( "ProviderTransform.options - gpt-5 textVerbosity" , ( ) = > {
const sessionID = "test-session-123"
const createGpt5Model = ( apiId : string ) = >
( {
id : ` openai/ ${ apiId } ` ,
providerID : "openai" ,
api : {
id : apiId ,
url : "https://api.openai.com" ,
npm : "@ai-sdk/openai" ,
} ,
name : apiId ,
capabilities : {
temperature : true ,
reasoning : true ,
attachment : true ,
toolcall : true ,
input : { text : true , audio : false , image : true , video : false , pdf : false } ,
output : { text : true , audio : false , image : false , video : false , pdf : false } ,
interleaved : false ,
} ,
cost : { input : 0.03 , output : 0.06 , cache : { read : 0.001 , write : 0.002 } } ,
limit : { context : 128000 , output : 4096 } ,
status : "active" ,
options : { } ,
headers : { } ,
} ) as any
test ( "gpt-5.2 should have textVerbosity set to low" , ( ) = > {
const model = createGpt5Model ( "gpt-5.2" )
const result = ProviderTransform . options ( { model , sessionID , providerOptions : { } } )
expect ( result . textVerbosity ) . toBe ( "low" )
2026-05-24 01:06:45 +00:00
expect ( result . include ) . toEqual ( [ "reasoning.encrypted_content" ] )
2026-01-30 20:57:01 +00:00
} )
2026-06-04 05:09:12 +00:00
test ( "Bedrock Mantle gpt-5.5 uses OpenAI Responses defaults" , ( ) = > {
const model = {
. . . createGpt5Model ( "openai.gpt-5.5" ) ,
id : "amazon-bedrock/openai.gpt-5.5" ,
providerID : "amazon-bedrock" ,
api : {
id : "openai.gpt-5.5" ,
url : "https://bedrock-mantle.us-east-2.api.aws/openai/v1" ,
npm : "@ai-sdk/amazon-bedrock/mantle" ,
} ,
}
const result = ProviderTransform . options ( { model , sessionID , providerOptions : { } } )
expect ( result . store ) . toBe ( false )
expect ( result . reasoningEffort ) . toBe ( "medium" )
expect ( result . reasoningSummary ) . toBe ( "auto" )
expect ( result . include ) . toEqual ( [ "reasoning.encrypted_content" ] )
expect ( result . textVerbosity ) . toBe ( "low" )
} )
2026-06-05 15:08:39 +00:00
test ( "openai-compatible gpt-5 models omit Responses-only reasoningSummary" , ( ) = > {
const model = {
. . . createGpt5Model ( "gpt-5.4" ) ,
id : "cortecs/gpt-5.4" ,
providerID : "cortecs" ,
api : {
id : "gpt-5.4" ,
url : "https://api.cortecs.ai/v1" ,
npm : "@ai-sdk/openai-compatible" ,
} ,
}
const result = ProviderTransform . options ( { model , sessionID , providerOptions : { } } )
expect ( result . reasoningEffort ) . toBe ( "medium" )
expect ( result . reasoningSummary ) . toBeUndefined ( )
expect ( result . include ) . toBeUndefined ( )
} )
test ( "azure chat completions omit Responses-only reasoning options after variants merge" , async ( ) = > {
const model = {
. . . createGpt5Model ( "gpt-5.4" ) ,
id : "azure/gpt-5.4" ,
providerID : "azure" ,
api : {
id : "gpt-5.4" ,
url : "https://azure.com" ,
npm : "@ai-sdk/azure" ,
} ,
variants : {
high : {
reasoningEffort : "high" ,
reasoningSummary : "auto" ,
include : [ "reasoning.encrypted_content" ] ,
} ,
} ,
}
const result = await Effect . runPromise (
LLMRequestPrep . prepare ( {
user : {
id : "msg_user-test" ,
sessionID ,
role : "user" ,
time : { created : Date.now ( ) } ,
agent : "test" ,
model : { providerID : "azure" , modelID : "gpt-5.4" , variant : "high" } ,
} as any ,
sessionID ,
model ,
agent : {
name : "test" ,
mode : "primary" ,
options : { } ,
permission : [ ] ,
} as any ,
system : [ ] ,
messages : [ { role : "user" , content : "Hello" } ] ,
2026-06-25 21:28:39 +00:00
tools : {
lookup : {
description : "Look up a value" ,
inputSchema : jsonSchema ( { type : "object" , properties : { } } ) ,
} ,
} ,
2026-06-05 15:08:39 +00:00
provider : { id : "azure" , options : { useCompletionUrls : true } } as any ,
auth : undefined ,
plugin : {
trigger : ( _name : string , _input : unknown , output : unknown ) = > Effect . succeed ( output ) ,
list : ( ) = > Effect . succeed ( [ ] ) ,
init : ( ) = > Effect . void ,
} as any ,
flags : { outputTokenMax : 32_000 , client : "test" } as any ,
isWorkflow : false ,
} ) ,
)
expect ( result . params . options . reasoningEffort ) . toBe ( "high" )
expect ( result . params . options . reasoningSummary ) . toBeUndefined ( )
expect ( result . params . options . include ) . toBeUndefined ( )
2026-06-25 21:28:39 +00:00
expect ( result . tools . lookup . strict ) . toBe ( false )
2026-06-05 15:08:39 +00:00
} )
2026-01-30 20:57:01 +00:00
test ( "gpt-5.1 should have textVerbosity set to low" , ( ) = > {
const model = createGpt5Model ( "gpt-5.1" )
const result = ProviderTransform . options ( { model , sessionID , providerOptions : { } } )
expect ( result . textVerbosity ) . toBe ( "low" )
} )
test ( "gpt-5.2-chat-latest should NOT have textVerbosity set (only supports medium)" , ( ) = > {
const model = createGpt5Model ( "gpt-5.2-chat-latest" )
const result = ProviderTransform . options ( { model , sessionID , providerOptions : { } } )
expect ( result . textVerbosity ) . toBeUndefined ( )
} )
test ( "gpt-5.1-chat-latest should NOT have textVerbosity set (only supports medium)" , ( ) = > {
const model = createGpt5Model ( "gpt-5.1-chat-latest" )
const result = ProviderTransform . options ( { model , sessionID , providerOptions : { } } )
expect ( result . textVerbosity ) . toBeUndefined ( )
} )
test ( "gpt-5.2-chat should NOT have textVerbosity set" , ( ) = > {
const model = createGpt5Model ( "gpt-5.2-chat" )
const result = ProviderTransform . options ( { model , sessionID , providerOptions : { } } )
expect ( result . textVerbosity ) . toBeUndefined ( )
} )
test ( "gpt-5-chat should NOT have textVerbosity set" , ( ) = > {
const model = createGpt5Model ( "gpt-5-chat" )
const result = ProviderTransform . options ( { model , sessionID , providerOptions : { } } )
expect ( result . textVerbosity ) . toBeUndefined ( )
} )
test ( "gpt-5.2-codex should NOT have textVerbosity set (codex models excluded)" , ( ) = > {
const model = createGpt5Model ( "gpt-5.2-codex" )
const result = ProviderTransform . options ( { model , sessionID , providerOptions : { } } )
expect ( result . textVerbosity ) . toBeUndefined ( )
} )
} )
2026-05-14 04:24:16 +00:00
describe ( "ProviderTransform.options - gpt-5 reasoningEffort" , ( ) = > {
const sessionID = "test-session-123"
const createModel = ( apiId : string ) = >
( {
id : ` azure/ ${ apiId } ` ,
providerID : "azure" ,
api : {
id : apiId ,
url : "https://azure.com" ,
npm : "@ai-sdk/azure" ,
} ,
name : apiId ,
capabilities : {
temperature : true ,
reasoning : true ,
attachment : true ,
toolcall : true ,
input : {
text : true ,
audio : false ,
image : true ,
video : false ,
pdf : false ,
} ,
output : {
text : true ,
audio : false ,
image : false ,
video : false ,
pdf : false ,
} ,
interleaved : false ,
} ,
cost : {
input : 0.03 ,
output : 0.06 ,
cache : { read : 0.001 , write : 0.002 } ,
} ,
limit : {
context : 128000 ,
output : 4096 ,
} ,
status : "active" ,
options : { } ,
headers : { } ,
} ) as any
2026-05-14 04:25:32 +00:00
test ( "gpt-5-chat should NOT set reasoningEffort" , ( ) = > {
2026-05-14 04:24:16 +00:00
const result = ProviderTransform . options ( {
model : createModel ( "gpt-5-chat" ) ,
sessionID ,
providerOptions : { } ,
} )
expect ( result . reasoningEffort ) . toBeUndefined ( )
} )
test ( "gpt-5.5 should NOT set reasoningEffort" , ( ) = > {
const result = ProviderTransform . options ( {
model : createModel ( "gpt-5.5" ) ,
sessionID ,
providerOptions : { } ,
} )
expect ( result . reasoningEffort ) . toBeUndefined ( )
} )
} )
2026-02-14 18:32:29 +00:00
describe ( "ProviderTransform.options - gateway" , ( ) = > {
const sessionID = "test-session-123"
const createModel = ( id : string ) = >
( {
id ,
providerID : "vercel" ,
api : {
id ,
url : "https://ai-gateway.vercel.sh/v3/ai" ,
npm : "@ai-sdk/gateway" ,
} ,
name : id ,
capabilities : {
temperature : true ,
reasoning : true ,
attachment : true ,
toolcall : true ,
input : { text : true , audio : false , image : true , video : false , pdf : true } ,
output : { text : true , audio : false , image : false , video : false , pdf : false } ,
interleaved : false ,
} ,
cost : {
input : 0.001 ,
output : 0.002 ,
cache : { read : 0.0001 , write : 0.0002 } ,
} ,
limit : {
context : 200_000 ,
output : 8192 ,
} ,
status : "active" ,
options : { } ,
headers : { } ,
release_date : "2024-01-01" ,
} ) as any
test ( "puts gateway defaults under gateway key" , ( ) = > {
const model = createModel ( "anthropic/claude-sonnet-4" )
const result = ProviderTransform . options ( { model , sessionID , providerOptions : { } } )
expect ( result ) . toEqual ( {
gateway : {
caching : "auto" ,
} ,
} )
} )
} )
describe ( "ProviderTransform.providerOptions" , ( ) = > {
const createModel = ( overrides : Partial < any > = { } ) = >
( {
id : "test/test-model" ,
providerID : "test" ,
api : {
id : "test-model" ,
url : "https://api.test.com" ,
npm : "@ai-sdk/openai" ,
} ,
name : "Test Model" ,
capabilities : {
temperature : true ,
reasoning : true ,
attachment : true ,
toolcall : true ,
input : { text : true , audio : false , image : true , video : false , pdf : false } ,
output : { text : true , audio : false , image : false , video : false , pdf : false } ,
interleaved : false ,
} ,
cost : {
input : 0.001 ,
output : 0.002 ,
cache : { read : 0.0001 , write : 0.0002 } ,
} ,
limit : {
context : 200_000 ,
output : 64_000 ,
} ,
status : "active" ,
options : { } ,
headers : { } ,
release_date : "2024-01-01" ,
. . . overrides ,
} ) as any
test ( "uses sdk key for non-gateway models" , ( ) = > {
const model = createModel ( {
providerID : "my-bedrock" ,
api : {
id : "anthropic.claude-sonnet-4" ,
url : "https://bedrock.aws" ,
npm : "@ai-sdk/amazon-bedrock" ,
} ,
} )
expect ( ProviderTransform . providerOptions ( model , { cachePoint : { type : "default" } } ) ) . toEqual ( {
bedrock : { cachePoint : { type : "default" } } ,
} )
} )
test ( "uses gateway model provider slug for gateway models" , ( ) = > {
const model = createModel ( {
providerID : "vercel" ,
api : {
id : "anthropic/claude-sonnet-4" ,
url : "https://ai-gateway.vercel.sh/v3/ai" ,
npm : "@ai-sdk/gateway" ,
} ,
} )
expect ( ProviderTransform . providerOptions ( model , { thinking : { type : "enabled" , budgetTokens : 12_000 } } ) ) . toEqual ( {
anthropic : { thinking : { type : "enabled" , budgetTokens : 12_000 } } ,
} )
} )
test ( "falls back to gateway key when gateway api id is unscoped" , ( ) = > {
const model = createModel ( {
id : "anthropic/claude-sonnet-4" ,
providerID : "vercel" ,
api : {
id : "claude-sonnet-4" ,
url : "https://ai-gateway.vercel.sh/v3/ai" ,
npm : "@ai-sdk/gateway" ,
} ,
} )
expect ( ProviderTransform . providerOptions ( model , { thinking : { type : "enabled" , budgetTokens : 12_000 } } ) ) . toEqual ( {
gateway : { thinking : { type : "enabled" , budgetTokens : 12_000 } } ,
} )
} )
test ( "splits gateway routing options from provider-specific options" , ( ) = > {
const model = createModel ( {
providerID : "vercel" ,
api : {
id : "anthropic/claude-sonnet-4" ,
url : "https://ai-gateway.vercel.sh/v3/ai" ,
npm : "@ai-sdk/gateway" ,
} ,
} )
expect (
ProviderTransform . providerOptions ( model , {
gateway : { order : [ "vertex" , "anthropic" ] } ,
thinking : { type : "enabled" , budgetTokens : 12_000 } ,
} ) ,
) . toEqual ( {
gateway : { order : [ "vertex" , "anthropic" ] } ,
anthropic : { thinking : { type : "enabled" , budgetTokens : 12_000 } } ,
} as any )
} )
test ( "falls back to gateway key when model id has no provider slug" , ( ) = > {
const model = createModel ( {
id : "claude-sonnet-4" ,
providerID : "vercel" ,
api : {
id : "claude-sonnet-4" ,
url : "https://ai-gateway.vercel.sh/v3/ai" ,
npm : "@ai-sdk/gateway" ,
} ,
} )
expect ( ProviderTransform . providerOptions ( model , { reasoningEffort : "high" } ) ) . toEqual ( {
gateway : { reasoningEffort : "high" } ,
} )
} )
2026-02-14 19:18:52 +00:00
test ( "maps amazon slug to bedrock for provider options" , ( ) = > {
const model = createModel ( {
providerID : "vercel" ,
api : {
id : "amazon/nova-2-lite" ,
url : "https://ai-gateway.vercel.sh/v3/ai" ,
npm : "@ai-sdk/gateway" ,
} ,
} )
expect ( ProviderTransform . providerOptions ( model , { reasoningConfig : { type : "enabled" } } ) ) . toEqual ( {
bedrock : { reasoningConfig : { type : "enabled" } } ,
} )
} )
2026-06-04 05:09:12 +00:00
test ( "maps Bedrock Mantle provider options to OpenAI namespace" , ( ) = > {
const model = createModel ( {
providerID : "amazon-bedrock" ,
api : {
id : "openai.gpt-5.5" ,
url : "https://bedrock-mantle.us-east-2.api.aws/openai/v1" ,
npm : "@ai-sdk/amazon-bedrock/mantle" ,
} ,
} )
expect ( ProviderTransform . providerOptions ( model , { reasoningEffort : "medium" } ) ) . toEqual ( {
openai : { reasoningEffort : "medium" } ,
} )
} )
2026-02-14 19:18:52 +00:00
test ( "uses groq slug for groq models" , ( ) = > {
const model = createModel ( {
providerID : "vercel" ,
api : {
id : "groq/llama-3.3-70b-versatile" ,
url : "https://ai-gateway.vercel.sh/v3/ai" ,
npm : "@ai-sdk/gateway" ,
} ,
} )
expect ( ProviderTransform . providerOptions ( model , { reasoningFormat : "parsed" } ) ) . toEqual ( {
groq : { reasoningFormat : "parsed" } ,
} )
} )
2026-02-14 18:32:29 +00:00
} )
2025-12-20 17:41:52 +00:00
describe ( "ProviderTransform.schema - gemini array items" , ( ) = > {
test ( "adds missing items for array properties" , ( ) = > {
const geminiModel = {
providerID : "google" ,
api : {
id : "gemini-3-pro" ,
} ,
} as any
const schema = {
type : "object" ,
properties : {
nodes : { type : "array" } ,
edges : { type : "array" , items : { type : "string" } } ,
} ,
} as any
const result = ProviderTransform . schema ( geminiModel , schema ) as any
expect ( result . properties . nodes . items ) . toBeDefined ( )
expect ( result . properties . edges . items . type ) . toBe ( "string" )
} )
} )
2026-02-03 15:24:52 +00:00
describe ( "ProviderTransform.schema - gemini nested array items" , ( ) = > {
const geminiModel = {
providerID : "google" ,
api : {
id : "gemini-3-pro" ,
} ,
} as any
test ( "adds type to 2D array with empty inner items" , ( ) = > {
const schema = {
type : "object" ,
properties : {
values : {
type : "array" ,
items : {
type : "array" ,
items : { } , // Empty items object
} ,
} ,
} ,
} as any
const result = ProviderTransform . schema ( geminiModel , schema ) as any
// Inner items should have a default type
expect ( result . properties . values . items . items . type ) . toBe ( "string" )
} )
test ( "adds items and type to 2D array with missing inner items" , ( ) = > {
const schema = {
type : "object" ,
properties : {
data : {
type : "array" ,
items : { type : "array" } , // No items at all
} ,
} ,
} as any
const result = ProviderTransform . schema ( geminiModel , schema ) as any
expect ( result . properties . data . items . items ) . toBeDefined ( )
expect ( result . properties . data . items . items . type ) . toBe ( "string" )
} )
test ( "handles deeply nested arrays (3D)" , ( ) = > {
const schema = {
type : "object" ,
properties : {
matrix : {
type : "array" ,
items : {
type : "array" ,
items : {
type : "array" ,
// No items
} ,
} ,
} ,
} ,
} as any
const result = ProviderTransform . schema ( geminiModel , schema ) as any
expect ( result . properties . matrix . items . items . items ) . toBeDefined ( )
expect ( result . properties . matrix . items . items . items . type ) . toBe ( "string" )
} )
test ( "preserves existing item types in nested arrays" , ( ) = > {
const schema = {
type : "object" ,
properties : {
numbers : {
type : "array" ,
items : {
type : "array" ,
items : { type : "number" } , // Has explicit type
} ,
} ,
} ,
} as any
const result = ProviderTransform . schema ( geminiModel , schema ) as any
// Should preserve the explicit type
expect ( result . properties . numbers . items . items . type ) . toBe ( "number" )
} )
test ( "handles mixed nested structures with objects and arrays" , ( ) = > {
const schema = {
type : "object" ,
properties : {
spreadsheetData : {
type : "object" ,
properties : {
rows : {
type : "array" ,
items : {
type : "array" ,
items : { } , // Empty items
} ,
} ,
} ,
} ,
} ,
} as any
const result = ProviderTransform . schema ( geminiModel , schema ) as any
expect ( result . properties . spreadsheetData . properties . rows . items . items . type ) . toBe ( "string" )
} )
} )
2026-06-11 16:30:50 +00:00
describe ( "ProviderTransform.schema - gemini type arrays" , ( ) = > {
// Mirrors @ai-sdk/google's convertJSONSchemaToOpenAPISchema: JSON Schema type
// arrays (e.g. `["number","string"]`, common in MCP tool schemas) become an
// `anyOf` of single-type schemas, with `null` lifted into `nullable`. Plain
// @ai-sdk/google rewrites these, but OpenAI-compatible transports such as
// GitHub Copilot (proxying to Gemini) forward them verbatim and the backend
// rejects the array form.
const geminiModel = {
providerID : "google" ,
api : {
id : "gemini-3-pro" ,
} ,
} as any
test ( "splits a multi-type array into anyOf and drops the type array" , ( ) = > {
const schema = {
type : "object" ,
properties : {
status : { type : [ "number" , "string" ] , description : "status filter" } ,
} ,
} as any
const result = ProviderTransform . schema ( geminiModel , schema ) as any
expect ( result . properties . status . type ) . toBeUndefined ( )
expect ( result . properties . status . anyOf ) . toEqual ( [ { type : "number" } , { type : "string" } ] )
expect ( result . properties . status . nullable ) . toBeUndefined ( )
// Sibling keywords stay alongside the generated anyOf.
expect ( result . properties . status . description ) . toBe ( "status filter" )
} )
test ( "lifts null into nullable for a nullable type array" , ( ) = > {
const schema = {
type : "object" ,
properties : {
maybe : { type : [ "string" , "null" ] , description : "nullable string" } ,
} ,
} as any
const result = ProviderTransform . schema ( geminiModel , schema ) as any
expect ( result . properties . maybe . type ) . toBeUndefined ( )
expect ( result . properties . maybe . anyOf ) . toEqual ( [ { type : "string" } ] )
expect ( result . properties . maybe . nullable ) . toBe ( true )
} )
test ( "collapses an all-null type array to type null" , ( ) = > {
const schema = {
type : "object" ,
properties : {
nothing : { type : [ "null" ] } ,
} ,
} as any
const result = ProviderTransform . schema ( geminiModel , schema ) as any
expect ( result . properties . nothing . type ) . toBe ( "null" )
expect ( result . properties . nothing . anyOf ) . toBeUndefined ( )
} )
test ( "rewrites type arrays for gemini served through github-copilot" , ( ) = > {
const copilotGeminiModel = {
providerID : "github-copilot" ,
api : {
id : "gemini-3.5-flash" ,
npm : "@ai-sdk/github-copilot" ,
} ,
} as any
const schema = {
type : "object" ,
properties : {
hook_id : { type : "number" , description : "ID of the webhook" } ,
status : { type : [ "number" , "string" ] , description : "Filter by response status code" } ,
} ,
required : [ "hook_id" ] ,
additionalProperties : false ,
} as any
const result = ProviderTransform . schema ( copilotGeminiModel , schema ) as any
expect ( result . properties . status . anyOf ) . toEqual ( [ { type : "number" } , { type : "string" } ] )
expect ( result . properties . status . type ) . toBeUndefined ( )
expect ( result . properties . hook_id . type ) . toBe ( "number" )
} )
} )
2026-03-03 04:41:05 +00:00
describe ( "ProviderTransform.schema - gemini combiner nodes" , ( ) = > {
const geminiModel = {
providerID : "google" ,
api : {
id : "gemini-3-pro" ,
} ,
} as any
const walk = ( node : any , cb : ( node : any , path : ( string | number ) [ ] ) = > void , path : ( string | number ) [ ] = [ ] ) = > {
if ( node === null || typeof node !== "object" ) {
return
}
if ( Array . isArray ( node ) ) {
node . forEach ( ( item , i ) = > walk ( item , cb , [ . . . path , i ] ) )
return
}
cb ( node , path )
Object . entries ( node ) . forEach ( ( [ key , value ] ) = > walk ( value , cb , [ . . . path , key ] ) )
}
test ( "keeps edits.items.anyOf without adding type" , ( ) = > {
const schema = {
type : "object" ,
properties : {
edits : {
type : "array" ,
items : {
anyOf : [
{
type : "object" ,
properties : {
old_string : { type : "string" } ,
new_string : { type : "string" } ,
} ,
required : [ "old_string" , "new_string" ] ,
} ,
{
type : "object" ,
properties : {
old_string : { type : "string" } ,
new_string : { type : "string" } ,
replace_all : { type : "boolean" } ,
} ,
required : [ "old_string" , "new_string" ] ,
} ,
] ,
} ,
} ,
} ,
required : [ "edits" ] ,
} as any
const result = ProviderTransform . schema ( geminiModel , schema ) as any
expect ( Array . isArray ( result . properties . edits . items . anyOf ) ) . toBe ( true )
expect ( result . properties . edits . items . type ) . toBeUndefined ( )
} )
test ( "does not add sibling keys to combiner nodes during sanitize" , ( ) = > {
const schema = {
type : "object" ,
properties : {
edits : {
type : "array" ,
items : {
anyOf : [ { type : "string" } , { type : "number" } ] ,
} ,
} ,
value : {
oneOf : [ { type : "string" } , { type : "boolean" } ] ,
} ,
meta : {
allOf : [
{
type : "object" ,
properties : { a : { type : "string" } } ,
} ,
{
type : "object" ,
properties : { b : { type : "string" } } ,
} ,
] ,
} ,
} ,
} as any
const input = JSON . parse ( JSON . stringify ( schema ) )
const result = ProviderTransform . schema ( geminiModel , schema ) as any
walk ( result , ( node , path ) = > {
const hasCombiner = Array . isArray ( node . anyOf ) || Array . isArray ( node . oneOf ) || Array . isArray ( node . allOf )
if ( ! hasCombiner ) {
return
}
const before = path . reduce ( ( acc : any , key ) = > acc ? . [ key ] , input )
const added = Object . keys ( node ) . filter ( ( key ) = > ! ( key in before ) )
expect ( added ) . toEqual ( [ ] )
} )
} )
} )
2026-02-03 05:13:24 +00:00
describe ( "ProviderTransform.schema - gemini non-object properties removal" , ( ) = > {
const geminiModel = {
providerID : "google" ,
api : {
id : "gemini-3-pro" ,
} ,
} as any
test ( "removes properties from non-object types" , ( ) = > {
const schema = {
type : "object" ,
properties : {
data : {
type : "string" ,
properties : { invalid : { type : "string" } } ,
} ,
} ,
} as any
const result = ProviderTransform . schema ( geminiModel , schema ) as any
expect ( result . properties . data . type ) . toBe ( "string" )
expect ( result . properties . data . properties ) . toBeUndefined ( )
} )
test ( "removes required from non-object types" , ( ) = > {
const schema = {
type : "object" ,
properties : {
data : {
type : "array" ,
items : { type : "string" } ,
required : [ "invalid" ] ,
} ,
} ,
} as any
const result = ProviderTransform . schema ( geminiModel , schema ) as any
expect ( result . properties . data . type ) . toBe ( "array" )
expect ( result . properties . data . required ) . toBeUndefined ( )
} )
test ( "removes properties and required from nested non-object types" , ( ) = > {
const schema = {
type : "object" ,
properties : {
outer : {
type : "object" ,
properties : {
inner : {
type : "number" ,
properties : { bad : { type : "string" } } ,
required : [ "bad" ] ,
} ,
} ,
} ,
} ,
} as any
const result = ProviderTransform . schema ( geminiModel , schema ) as any
expect ( result . properties . outer . properties . inner . type ) . toBe ( "number" )
expect ( result . properties . outer . properties . inner . properties ) . toBeUndefined ( )
expect ( result . properties . outer . properties . inner . required ) . toBeUndefined ( )
} )
test ( "keeps properties and required on object types" , ( ) = > {
const schema = {
type : "object" ,
properties : {
data : {
type : "object" ,
properties : { name : { type : "string" } } ,
required : [ "name" ] ,
} ,
} ,
} as any
const result = ProviderTransform . schema ( geminiModel , schema ) as any
expect ( result . properties . data . type ) . toBe ( "object" )
expect ( result . properties . data . properties ) . toBeDefined ( )
expect ( result . properties . data . required ) . toEqual ( [ "name" ] )
} )
test ( "does not affect non-gemini providers" , ( ) = > {
const openaiModel = {
providerID : "openai" ,
api : {
id : "gpt-4" ,
} ,
} as any
const schema = {
type : "object" ,
properties : {
data : {
type : "string" ,
properties : { invalid : { type : "string" } } ,
} ,
} ,
} as any
const result = ProviderTransform . schema ( openaiModel , schema ) as any
expect ( result . properties . data . properties ) . toBeDefined ( )
} )
} )
2026-06-17 00:00:58 +00:00
describe ( "ProviderTransform.schema - openai supported schema subset" , ( ) = > {
const openaiModel = {
providerID : "openai" ,
api : {
id : "gpt-4.1" ,
npm : "@ai-sdk/openai" ,
} ,
} as any
test ( "removes unsupported JSON Schema keywords recursively" , ( ) = > {
const result = ProviderTransform . schema ( openaiModel , {
$schema : "https://json-schema.org/draft/2020-12/schema" ,
title : "Search" ,
type : "object" ,
properties : {
query : {
type : "string" ,
description : "Search query" ,
format : "uri" ,
pattern : "^https://" ,
minLength : 1 ,
maxLength : 100 ,
default : "https://example.com" ,
} ,
count : {
type : "integer" ,
minimum : 1 ,
maximum : 10 ,
multipleOf : 1 ,
} ,
createdAt : {
format : "date-time" ,
} ,
mode : {
const : "fast" ,
} ,
tags : {
type : "array" ,
minItems : 1 ,
maxItems : 3 ,
uniqueItems : true ,
} ,
tuple : {
type : "array" ,
items : [
{ type : "number" , minimum : 0 } ,
{ type : "string" , pattern : "^ok$" } ,
] ,
} ,
metadata : {
type : "object" ,
patternProperties : {
"^x-" : { type : "string" } ,
} ,
additionalProperties : {
type : "string" ,
pattern : "^safe$" ,
} ,
} ,
} ,
patternProperties : {
"^extra" : { type : "string" } ,
} ,
required : [ "query" ] ,
additionalProperties : false ,
} as any ) as any
expect ( result ) . toEqual ( {
type : "object" ,
properties : {
query : {
type : "string" ,
description : "Search query" ,
} ,
count : {
type : "integer" ,
} ,
createdAt : {
type : "string" ,
} ,
mode : {
enum : [ "fast" ] ,
type : "string" ,
} ,
tags : {
type : "array" ,
items : { type : "string" } ,
} ,
tuple : {
type : "array" ,
items : [ { type : "number" } , { type : "string" } ] ,
} ,
metadata : {
type : "object" ,
properties : { } ,
additionalProperties : {
type : "string" ,
} ,
} ,
} ,
required : [ "query" ] ,
additionalProperties : false ,
} )
} )
test ( "keeps local references and sanitizes definitions" , ( ) = > {
const result = ProviderTransform . schema ( openaiModel , {
type : "object" ,
properties : {
value : {
$ref : "#/$defs/Value" ,
description : "Referenced value" ,
examples : [ "ignored" ] ,
} ,
} ,
$defs : {
Value : {
type : "string" ,
pattern : "^value$" ,
description : "Definition description" ,
} ,
Unused : {
type : "number" ,
minimum : 0 ,
} ,
} ,
} as any ) as any
expect ( result . properties . value ) . toEqual ( {
$ref : "#/$defs/Value" ,
description : "Referenced value" ,
} )
expect ( result . $defs ) . toEqual ( {
Value : {
type : "string" ,
description : "Definition description" ,
} ,
Unused : {
type : "number" ,
} ,
} )
} )
test ( "does not sanitize non-openai providers" , ( ) = > {
const result = ProviderTransform . schema (
{
providerID : "anthropic" ,
api : {
id : "claude-sonnet-4" ,
npm : "@ai-sdk/anthropic" ,
} ,
} as any ,
{
type : "object" ,
properties : {
query : {
type : "string" ,
pattern : "^https://" ,
} ,
} ,
} as any ,
) as any
expect ( result . properties . query . pattern ) . toBe ( "^https://" )
} )
test . each ( [
[ "opencode" , "@ai-sdk/openai" ] ,
[ "custom-openai-compatible" , "@ai-sdk/openai" ] ,
[ "azure" , "@ai-sdk/azure" ] ,
] ) ( "sanitizes %s models using %s" , ( providerID , npm ) = > {
expect (
ProviderTransform . schema (
{
providerID ,
api : {
id : "custom-model" ,
npm ,
} ,
} as any ,
{
type : "object" ,
properties : {
query : {
type : "string" ,
pattern : "^https://" ,
} ,
} ,
} as any ,
) ,
) . toEqual ( {
type : "object" ,
properties : {
query : {
type : "string" ,
} ,
} ,
} )
} )
} )
2026-04-28 04:44:21 +00:00
describe ( "ProviderTransform.schema - moonshot $ref siblings" , ( ) = > {
const moonshotModel = {
providerID : "moonshotai" ,
api : {
id : "kimi-k2" ,
} ,
} as any
test ( "removes sibling descriptions from referenced tool parameter schemas" , ( ) = > {
const schema = {
type : "object" ,
properties : {
deviceType : {
description : "Optional. The type of device that captured the screenshot, e.g. mobile or desktop." ,
enum : [ "DEVICE_TYPE_UNSPECIFIED" , "MOBILE" , "DESKTOP" , "TABLET" , "AGNOSTIC" ] ,
type : "string" ,
} ,
modelId : {
description : "Optional. The model to use for generation." ,
enum : [ "MODEL_ID_UNSPECIFIED" , "GEMINI_3_PRO" , "GEMINI_3_FLASH" , "GEMINI_3_1_PRO" ] ,
type : "string" ,
} ,
projectId : {
description : "Required. The project ID of screens to generate variants for." ,
type : "string" ,
} ,
prompt : {
description : "Required. The input text used to generate the variants." ,
type : "string" ,
} ,
selectedScreenIds : {
description : "Required. The screen ids of screen to generate variants for." ,
items : {
type : "string" ,
} ,
type : "array" ,
} ,
variantOptions : {
$ref : "#/$defs/VariantOptions" ,
description :
"Required. The variant options for generation, including the number of variants, creative range, and aspects to focus on." ,
} ,
} ,
required : [ "projectId" , "selectedScreenIds" , "prompt" , "variantOptions" ] ,
$defs : {
VariantOptions : {
description :
"Configuration options for design variant generation. This message captures all parameters used to generate variants, allowing the configuration to be stored, replayed, or analyzed." ,
properties : {
aspects : {
description : "Optional. Specific aspects to focus on. If empty, all aspects may be varied." ,
items : {
2026-04-28 04:45:24 +00:00
enum : [ "VARIANT_ASPECT_UNSPECIFIED" , "LAYOUT" , "COLOR_SCHEME" , "IMAGES" , "TEXT_FONT" , "TEXT_CONTENT" ] ,
2026-04-28 04:44:21 +00:00
type : "string" ,
} ,
type : "array" ,
} ,
creativeRange : {
description : "Optional. Creative range for variations. Default: EXPLORE" ,
enum : [ "CREATIVE_RANGE_UNSPECIFIED" , "REFINE" , "EXPLORE" , "REIMAGINE" ] ,
type : "string" ,
} ,
variantCount : {
description : "Optional. Number of variants to generate (1-5). Default: 3" ,
format : "int32" ,
type : "integer" ,
} ,
} ,
type : "object" ,
} ,
} ,
description : "Request message for GenerateVariants." ,
additionalProperties : false ,
} as any
const result = ProviderTransform . schema ( moonshotModel , schema ) as any
expect ( result . properties . variantOptions ) . toEqual ( {
$ref : "#/$defs/VariantOptions" ,
} )
expect ( result . $defs . VariantOptions . description ) . toBe ( schema . $defs . VariantOptions . description )
} )
test ( "also runs for kimi models outside the moonshot provider" , ( ) = > {
const result = ProviderTransform . schema (
{
providerID : "openrouter" ,
name : "Kimi K2" ,
api : {
id : "moonshotai/kimi-k2" ,
} ,
} as any ,
{
type : "object" ,
properties : {
value : {
$ref : "#/$defs/Value" ,
description : "Moonshot rejects this sibling after ref expansion." ,
} ,
} ,
$defs : {
Value : {
description : "Referenced schema description stays here." ,
type : "object" ,
} ,
} ,
} as any ,
) as any
expect ( result . properties . value ) . toEqual ( {
$ref : "#/$defs/Value" ,
} )
} )
test ( "converts tuple-style array items to a single item schema" , ( ) = > {
2026-04-28 04:45:24 +00:00
const result = ProviderTransform . schema ( moonshotModel , {
type : "object" ,
properties : {
codeSpec : {
type : "object" ,
properties : {
accessibility : {
type : "object" ,
properties : {
renderedSize : {
description : "Rendered size [width, height] in px" ,
type : "array" ,
items : [ { type : "number" } , { type : "number" } ] ,
minItems : 2 ,
maxItems : 2 ,
2026-04-28 04:44:21 +00:00
} ,
} ,
} ,
} ,
} ,
2026-04-28 04:45:24 +00:00
} ,
} as any ) as any
2026-04-28 04:44:21 +00:00
expect ( result . properties . codeSpec . properties . accessibility . properties . renderedSize . items ) . toEqual ( {
type : "number" ,
} )
} )
} )
2025-12-04 03:31:36 +00:00
describe ( "ProviderTransform.message - DeepSeek reasoning content" , ( ) = > {
test ( "DeepSeek with tool calls includes reasoning_content in providerOptions" , ( ) = > {
const msgs = [
{
role : "assistant" ,
content : [
{ type : "reasoning" , text : "Let me think about this..." } ,
{
type : "tool-call" ,
toolCallId : "test" ,
toolName : "bash" ,
input : { command : "echo hello" } ,
} ,
] ,
} ,
] as any [ ]
2026-01-15 07:31:50 +00:00
const result = ProviderTransform . message (
msgs ,
{
2026-06-04 06:57:43 +00:00
id : ModelV2.ID.make ( "deepseek/deepseek-chat" ) ,
2026-05-31 01:08:38 +00:00
providerID : ProviderV2.ID.make ( "deepseek" ) ,
2026-01-15 07:31:50 +00:00
api : {
id : "deepseek-chat" ,
url : "https://api.deepseek.com" ,
npm : "@ai-sdk/openai-compatible" ,
2025-12-19 22:37:36 +00:00
} ,
2026-01-15 07:31:50 +00:00
name : "DeepSeek Chat" ,
capabilities : {
temperature : true ,
reasoning : true ,
attachment : false ,
toolcall : true ,
input : { text : true , audio : false , image : false , video : false , pdf : false } ,
output : { text : true , audio : false , image : false , video : false , pdf : false } ,
interleaved : {
field : "reasoning_content" ,
} ,
} ,
cost : {
input : 0.001 ,
output : 0.002 ,
cache : { read : 0.0001 , write : 0.0002 } ,
} ,
limit : {
context : 128000 ,
output : 8192 ,
} ,
status : "active" ,
options : { } ,
headers : { } ,
release_date : "2023-04-01" ,
2025-12-04 03:31:36 +00:00
} ,
2026-01-15 07:31:50 +00:00
{ } ,
)
2025-12-04 03:31:36 +00:00
expect ( result ) . toHaveLength ( 1 )
expect ( result [ 0 ] . content ) . toEqual ( [
{
type : "tool-call" ,
toolCallId : "test" ,
toolName : "bash" ,
input : { command : "echo hello" } ,
} ,
] )
expect ( result [ 0 ] . providerOptions ? . openaiCompatible ? . reasoning_content ) . toBe ( "Let me think about this..." )
} )
test ( "Non-DeepSeek providers leave reasoning content unchanged" , ( ) = > {
const msgs = [
{
role : "assistant" ,
content : [
{ type : "reasoning" , text : "Should not be processed" } ,
{ type : "text" , text : "Answer" } ,
] ,
} ,
] as any [ ]
2026-01-15 07:31:50 +00:00
const result = ProviderTransform . message (
msgs ,
{
2026-06-04 06:57:43 +00:00
id : ModelV2.ID.make ( "openai/gpt-4" ) ,
2026-05-31 01:08:38 +00:00
providerID : ProviderV2.ID.make ( "openai" ) ,
2026-01-15 07:31:50 +00:00
api : {
id : "gpt-4" ,
url : "https://api.openai.com" ,
npm : "@ai-sdk/openai" ,
} ,
name : "GPT-4" ,
capabilities : {
temperature : true ,
reasoning : false ,
attachment : true ,
toolcall : true ,
input : { text : true , audio : false , image : true , video : false , pdf : false } ,
output : { text : true , audio : false , image : false , video : false , pdf : false } ,
interleaved : false ,
} ,
cost : {
input : 0.03 ,
output : 0.06 ,
cache : { read : 0.001 , write : 0.002 } ,
} ,
limit : {
context : 128000 ,
output : 4096 ,
} ,
status : "active" ,
options : { } ,
headers : { } ,
release_date : "2023-04-01" ,
2025-12-04 03:31:36 +00:00
} ,
2026-01-15 07:31:50 +00:00
{ } ,
)
2025-12-04 03:31:36 +00:00
expect ( result [ 0 ] . content ) . toEqual ( [
{ type : "reasoning" , text : "Should not be processed" } ,
{ type : "text" , text : "Answer" } ,
] )
expect ( result [ 0 ] . providerOptions ? . openaiCompatible ? . reasoning_content ) . toBeUndefined ( )
} )
} )
2025-12-15 05:56:47 +00:00
2026-05-05 23:07:23 +00:00
describe ( "ProviderTransform.message - surrogate sanitization" , ( ) = > {
const model = {
id : "test/test-model" ,
providerID : "test" ,
api : {
id : "test-model" ,
url : "https://api.test.com" ,
npm : "@ai-sdk/openai-compatible" ,
} ,
name : "Test Model" ,
capabilities : {
temperature : true ,
reasoning : true ,
attachment : true ,
toolcall : true ,
input : { text : true , audio : false , image : true , video : false , pdf : false } ,
output : { text : true , audio : false , image : false , video : false , pdf : false } ,
interleaved : false ,
} ,
cost : { input : 0.001 , output : 0.002 , cache : { read : 0.0001 , write : 0.0002 } } ,
limit : { context : 128000 , output : 8192 } ,
status : "active" ,
options : { } ,
headers : { } ,
} as any
test ( "replaces lone surrogates in model-visible text" , ( ) = > {
const lone = "\uD83D"
const valid = "🚀"
const sanitized = "<22> "
const text = ( label : string ) = > ` ${ label } ${ lone } and ${ valid } `
const expected = ( label : string ) = > ` ${ label } ${ sanitized } and ${ valid } `
const msgs = [
{ role : "system" , content : text ( "system" ) } ,
{ role : "user" , content : text ( "user string" ) } ,
{
role : "user" ,
content : [
{ type : "text" , text : text ( "user text" ) } ,
{ type : "image" , image : "data:image/png;base64,abcd" } ,
] ,
} ,
{ role : "assistant" , content : text ( "assistant string" ) } ,
{
role : "assistant" ,
content : [
{ type : "text" , text : text ( "assistant text" ) } ,
{ type : "reasoning" , text : text ( "assistant reasoning" ) } ,
{ type : "tool-call" , toolCallId : "call-1" , toolName : "Read" , input : { filePath : ".opencode/tool/emoji.ts" } } ,
2026-05-05 23:08:37 +00:00
{
type : "tool-result" ,
toolCallId : "call-2" ,
toolName : "Read" ,
output : { type : "text" , value : text ( "assistant tool text" ) } ,
} ,
{
type : "tool-result" ,
toolCallId : "call-3" ,
toolName : "Read" ,
output : { type : "error-text" , value : text ( "assistant tool error" ) } ,
} ,
2026-05-05 23:07:23 +00:00
{
type : "tool-result" ,
toolCallId : "call-4" ,
toolName : "Read" ,
output : { type : "content" , value : [ { type : "text" , text : text ( "assistant tool content" ) } ] } ,
} ,
] ,
} ,
{
role : "tool" ,
content : [
2026-05-05 23:08:37 +00:00
{
type : "tool-result" ,
toolCallId : "call-5" ,
toolName : "Read" ,
output : { type : "text" , value : text ( "tool text" ) } ,
} ,
{
type : "tool-result" ,
toolCallId : "call-6" ,
toolName : "Read" ,
output : { type : "error-text" , value : text ( "tool error" ) } ,
} ,
2026-05-05 23:07:23 +00:00
{
type : "tool-result" ,
toolCallId : "call-7" ,
toolName : "Read" ,
output : { type : "content" , value : [ { type : "text" , text : text ( "tool content" ) } ] } ,
} ,
] ,
} ,
] as any [ ]
const result = ProviderTransform . message ( msgs , model , { } ) as any [ ]
expect ( result [ 0 ] . content ) . toBe ( expected ( "system" ) )
expect ( result [ 1 ] . content ) . toBe ( expected ( "user string" ) )
expect ( result [ 2 ] . content [ 0 ] . text ) . toBe ( expected ( "user text" ) )
expect ( result [ 3 ] . content ) . toBe ( expected ( "assistant string" ) )
expect ( result [ 4 ] . content [ 0 ] . text ) . toBe ( expected ( "assistant text" ) )
expect ( result [ 4 ] . content [ 1 ] . text ) . toBe ( expected ( "assistant reasoning" ) )
expect ( result [ 4 ] . content [ 3 ] . output . value ) . toBe ( expected ( "assistant tool text" ) )
expect ( result [ 4 ] . content [ 4 ] . output . value ) . toBe ( expected ( "assistant tool error" ) )
expect ( result [ 4 ] . content [ 5 ] . output . value [ 0 ] . text ) . toBe ( expected ( "assistant tool content" ) )
expect ( result [ 5 ] . content [ 0 ] . output . value ) . toBe ( expected ( "tool text" ) )
expect ( result [ 5 ] . content [ 1 ] . output . value ) . toBe ( expected ( "tool error" ) )
expect ( result [ 5 ] . content [ 2 ] . output . value [ 0 ] . text ) . toBe ( expected ( "tool content" ) )
expect ( result [ 2 ] . content [ 1 ] ) . toEqual ( { type : "image" , image : "data:image/png;base64,abcd" } )
} )
} )
2025-12-15 05:56:47 +00:00
describe ( "ProviderTransform.message - empty image handling" , ( ) = > {
const mockModel = {
id : "anthropic/claude-3-5-sonnet" ,
providerID : "anthropic" ,
api : {
id : "claude-3-5-sonnet-20241022" ,
url : "https://api.anthropic.com" ,
npm : "@ai-sdk/anthropic" ,
} ,
name : "Claude 3.5 Sonnet" ,
capabilities : {
temperature : true ,
reasoning : false ,
attachment : true ,
toolcall : true ,
input : { text : true , audio : false , image : true , video : false , pdf : true } ,
output : { text : true , audio : false , image : false , video : false , pdf : false } ,
interleaved : false ,
} ,
cost : {
input : 0.003 ,
output : 0.015 ,
cache : { read : 0.0003 , write : 0.00375 } ,
} ,
limit : {
context : 200000 ,
output : 8192 ,
} ,
status : "active" ,
options : { } ,
headers : { } ,
} as any
test ( "should replace empty base64 image with error text" , ( ) = > {
const msgs = [
{
role : "user" ,
content : [
{ type : "text" , text : "What is in this image?" } ,
{ type : "image" , image : "data:image/png;base64," } ,
] ,
} ,
] as any [ ]
2026-01-15 07:31:50 +00:00
const result = ProviderTransform . message ( msgs , mockModel , { } )
2025-12-15 05:56:47 +00:00
expect ( result ) . toHaveLength ( 1 )
expect ( result [ 0 ] . content ) . toHaveLength ( 2 )
expect ( result [ 0 ] . content [ 0 ] ) . toEqual ( { type : "text" , text : "What is in this image?" } )
expect ( result [ 0 ] . content [ 1 ] ) . toEqual ( {
type : "text" ,
text : "ERROR: Image file is empty or corrupted. Please provide a valid image." ,
} )
} )
test ( "should keep valid base64 images unchanged" , ( ) = > {
const validBase64 =
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
const msgs = [
{
role : "user" ,
content : [
{ type : "text" , text : "What is in this image?" } ,
{ type : "image" , image : ` data:image/png;base64, ${ validBase64 } ` } ,
] ,
} ,
] as any [ ]
2026-01-15 07:31:50 +00:00
const result = ProviderTransform . message ( msgs , mockModel , { } )
2025-12-15 05:56:47 +00:00
expect ( result ) . toHaveLength ( 1 )
expect ( result [ 0 ] . content ) . toHaveLength ( 2 )
expect ( result [ 0 ] . content [ 0 ] ) . toEqual ( { type : "text" , text : "What is in this image?" } )
expect ( result [ 0 ] . content [ 1 ] ) . toEqual ( { type : "image" , image : ` data:image/png;base64, ${ validBase64 } ` } )
} )
test ( "should handle mixed valid and empty images" , ( ) = > {
const validBase64 =
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
const msgs = [
{
role : "user" ,
content : [
{ type : "text" , text : "Compare these images" } ,
{ type : "image" , image : ` data:image/png;base64, ${ validBase64 } ` } ,
{ type : "image" , image : "data:image/jpeg;base64," } ,
] ,
} ,
] as any [ ]
2026-01-15 07:31:50 +00:00
const result = ProviderTransform . message ( msgs , mockModel , { } )
2025-12-15 05:56:47 +00:00
expect ( result ) . toHaveLength ( 1 )
expect ( result [ 0 ] . content ) . toHaveLength ( 3 )
expect ( result [ 0 ] . content [ 0 ] ) . toEqual ( { type : "text" , text : "Compare these images" } )
expect ( result [ 0 ] . content [ 1 ] ) . toEqual ( { type : "image" , image : ` data:image/png;base64, ${ validBase64 } ` } )
expect ( result [ 0 ] . content [ 2 ] ) . toEqual ( {
type : "text" ,
text : "ERROR: Image file is empty or corrupted. Please provide a valid image." ,
} )
} )
} )
2025-12-30 22:37:32 +00:00
2026-01-05 07:18:46 +00:00
describe ( "ProviderTransform.message - anthropic empty content filtering" , ( ) = > {
const anthropicModel = {
id : "anthropic/claude-3-5-sonnet" ,
providerID : "anthropic" ,
api : {
id : "claude-3-5-sonnet-20241022" ,
url : "https://api.anthropic.com" ,
npm : "@ai-sdk/anthropic" ,
} ,
name : "Claude 3.5 Sonnet" ,
capabilities : {
temperature : true ,
reasoning : false ,
attachment : true ,
toolcall : true ,
input : { text : true , audio : false , image : true , video : false , pdf : true } ,
output : { text : true , audio : false , image : false , video : false , pdf : false } ,
interleaved : false ,
} ,
cost : {
input : 0.003 ,
output : 0.015 ,
cache : { read : 0.0003 , write : 0.00375 } ,
} ,
limit : {
context : 200000 ,
output : 8192 ,
} ,
status : "active" ,
options : { } ,
headers : { } ,
} as any
test ( "filters out messages with empty string content" , ( ) = > {
const msgs = [
{ role : "user" , content : "Hello" } ,
{ role : "assistant" , content : "" } ,
{ role : "user" , content : "World" } ,
] as any [ ]
2026-01-15 07:31:50 +00:00
const result = ProviderTransform . message ( msgs , anthropicModel , { } )
2026-01-05 07:18:46 +00:00
expect ( result ) . toHaveLength ( 2 )
expect ( result [ 0 ] . content ) . toBe ( "Hello" )
expect ( result [ 1 ] . content ) . toBe ( "World" )
} )
test ( "filters out empty text parts from array content" , ( ) = > {
const msgs = [
{
role : "assistant" ,
content : [
{ type : "text" , text : "" } ,
{ type : "text" , text : "Hello" } ,
{ type : "text" , text : "" } ,
] ,
} ,
] as any [ ]
2026-01-15 07:31:50 +00:00
const result = ProviderTransform . message ( msgs , anthropicModel , { } )
2026-01-05 07:18:46 +00:00
expect ( result ) . toHaveLength ( 1 )
expect ( result [ 0 ] . content ) . toHaveLength ( 1 )
expect ( result [ 0 ] . content [ 0 ] ) . toEqual ( { type : "text" , text : "Hello" } )
} )
test ( "filters out empty reasoning parts from array content" , ( ) = > {
const msgs = [
{
role : "assistant" ,
content : [
{ type : "reasoning" , text : "" } ,
{ type : "text" , text : "Answer" } ,
{ type : "reasoning" , text : "" } ,
] ,
} ,
] as any [ ]
2026-01-15 07:31:50 +00:00
const result = ProviderTransform . message ( msgs , anthropicModel , { } )
2026-01-05 07:18:46 +00:00
expect ( result ) . toHaveLength ( 1 )
expect ( result [ 0 ] . content ) . toHaveLength ( 1 )
expect ( result [ 0 ] . content [ 0 ] ) . toEqual ( { type : "text" , text : "Answer" } )
} )
test ( "removes entire message when all parts are empty" , ( ) = > {
const msgs = [
{ role : "user" , content : "Hello" } ,
{
role : "assistant" ,
content : [
{ type : "text" , text : "" } ,
{ type : "reasoning" , text : "" } ,
] ,
} ,
{ role : "user" , content : "World" } ,
] as any [ ]
2026-01-15 07:31:50 +00:00
const result = ProviderTransform . message ( msgs , anthropicModel , { } )
2026-01-05 07:18:46 +00:00
expect ( result ) . toHaveLength ( 2 )
expect ( result [ 0 ] . content ) . toBe ( "Hello" )
expect ( result [ 1 ] . content ) . toBe ( "World" )
} )
test ( "keeps non-text/reasoning parts even if text parts are empty" , ( ) = > {
const msgs = [
{
role : "assistant" ,
content : [
{ type : "text" , text : "" } ,
{ type : "tool-call" , toolCallId : "123" , toolName : "bash" , input : { command : "ls" } } ,
] ,
} ,
] as any [ ]
2026-01-15 07:31:50 +00:00
const result = ProviderTransform . message ( msgs , anthropicModel , { } )
2026-01-05 07:18:46 +00:00
expect ( result ) . toHaveLength ( 1 )
expect ( result [ 0 ] . content ) . toHaveLength ( 1 )
expect ( result [ 0 ] . content [ 0 ] ) . toEqual ( {
type : "tool-call" ,
toolCallId : "123" ,
toolName : "bash" ,
input : { command : "ls" } ,
} )
} )
test ( "keeps messages with valid text alongside empty parts" , ( ) = > {
const msgs = [
{
role : "assistant" ,
content : [
{ type : "reasoning" , text : "Thinking..." } ,
{ type : "text" , text : "" } ,
{ type : "text" , text : "Result" } ,
] ,
} ,
] as any [ ]
2026-01-15 07:31:50 +00:00
const result = ProviderTransform . message ( msgs , anthropicModel , { } )
2026-01-05 07:18:46 +00:00
expect ( result ) . toHaveLength ( 1 )
expect ( result [ 0 ] . content ) . toHaveLength ( 2 )
expect ( result [ 0 ] . content [ 0 ] ) . toEqual ( { type : "reasoning" , text : "Thinking..." } )
expect ( result [ 0 ] . content [ 1 ] ) . toEqual ( { type : "text" , text : "Result" } )
} )
2026-03-13 03:13:09 +00:00
test ( "filters empty content for bedrock provider" , ( ) = > {
const bedrockModel = {
. . . anthropicModel ,
id : "amazon-bedrock/anthropic.claude-opus-4-6" ,
providerID : "amazon-bedrock" ,
api : {
id : "anthropic.claude-opus-4-6" ,
url : "https://bedrock-runtime.us-east-1.amazonaws.com" ,
npm : "@ai-sdk/amazon-bedrock" ,
} ,
}
const msgs = [
{ role : "user" , content : "Hello" } ,
{ role : "assistant" , content : "" } ,
{
role : "assistant" ,
content : [
{ type : "text" , text : "" } ,
{ type : "text" , text : "Answer" } ,
] ,
} ,
] as any [ ]
const result = ProviderTransform . message ( msgs , bedrockModel , { } )
expect ( result ) . toHaveLength ( 2 )
expect ( result [ 0 ] . content ) . toBe ( "Hello" )
expect ( result [ 1 ] . content ) . toHaveLength ( 1 )
expect ( result [ 1 ] . content [ 0 ] ) . toEqual ( { type : "text" , text : "Answer" } )
} )
2026-01-05 07:18:46 +00:00
test ( "does not filter for non-anthropic providers" , ( ) = > {
const openaiModel = {
. . . anthropicModel ,
providerID : "openai" ,
api : {
id : "gpt-4" ,
url : "https://api.openai.com" ,
npm : "@ai-sdk/openai" ,
} ,
}
const msgs = [
{ role : "assistant" , content : "" } ,
{
role : "assistant" ,
content : [ { type : "text" , text : "" } ] ,
} ,
] as any [ ]
2026-01-15 07:31:50 +00:00
const result = ProviderTransform . message ( msgs , openaiModel , { } )
2026-01-05 07:18:46 +00:00
expect ( result ) . toHaveLength ( 2 )
expect ( result [ 0 ] . content ) . toBe ( "" )
expect ( result [ 1 ] . content ) . toHaveLength ( 1 )
} )
2026-04-15 19:56:45 +00:00
test ( "leaves valid anthropic assistant tool ordering unchanged" , ( ) = > {
const msgs = [
{
role : "assistant" ,
content : [
{ type : "text" , text : "I checked your home directory and looked for PDF files." } ,
{ type : "tool-call" , toolCallId : "toolu_1" , toolName : "read" , input : { filePath : "/root" } } ,
{ type : "tool-call" , toolCallId : "toolu_2" , toolName : "glob" , input : { pattern : "**/*.pdf" } } ,
] ,
} ,
] as any [ ]
const result = ProviderTransform . message ( msgs , anthropicModel , { } ) as any [ ]
expect ( result ) . toHaveLength ( 1 )
expect ( result [ 0 ] . content ) . toMatchObject ( [
{ type : "text" , text : "I checked your home directory and looked for PDF files." } ,
{ type : "tool-call" , toolCallId : "toolu_1" , toolName : "read" , input : { filePath : "/root" } } ,
{ type : "tool-call" , toolCallId : "toolu_2" , toolName : "glob" , input : { pattern : "**/*.pdf" } } ,
] )
} )
2026-01-05 07:18:46 +00:00
} )
2026-01-15 07:31:50 +00:00
describe ( "ProviderTransform.message - strip openai metadata when store=false" , ( ) = > {
const openaiModel = {
id : "openai/gpt-5" ,
providerID : "openai" ,
api : {
id : "gpt-5" ,
url : "https://api.openai.com" ,
npm : "@ai-sdk/openai" ,
} ,
name : "GPT-5" ,
capabilities : {
temperature : true ,
reasoning : true ,
attachment : true ,
toolcall : true ,
input : { text : true , audio : false , image : true , video : false , pdf : false } ,
output : { text : true , audio : false , image : false , video : false , pdf : false } ,
interleaved : false ,
} ,
cost : { input : 0.03 , output : 0.06 , cache : { read : 0.001 , write : 0.002 } } ,
limit : { context : 128000 , output : 4096 } ,
status : "active" ,
options : { } ,
headers : { } ,
} as any
2026-06-08 22:43:48 +00:00
test ( "strips OpenAI itemId and preserves reasoningEncryptedContent when store=false" , ( ) = > {
2026-01-15 07:31:50 +00:00
const msgs = [
{
role : "assistant" ,
content : [
{
type : "reasoning" ,
text : "thinking..." ,
providerOptions : {
openai : {
itemId : "rs_123" ,
reasoningEncryptedContent : "encrypted" ,
} ,
} ,
} ,
{
type : "text" ,
text : "Hello" ,
providerOptions : {
openai : {
itemId : "msg_456" ,
} ,
} ,
} ,
] ,
} ,
] as any [ ]
const result = ProviderTransform . message ( msgs , openaiModel , { store : false } ) as any [ ]
expect ( result ) . toHaveLength ( 1 )
2026-06-08 22:43:48 +00:00
expect ( result [ 0 ] . content [ 0 ] . providerOptions ? . openai ? . itemId ) . toBeUndefined ( )
expect ( result [ 0 ] . content [ 0 ] . providerOptions ? . openai ? . reasoningEncryptedContent ) . toBe ( "encrypted" )
expect ( result [ 0 ] . content [ 1 ] . providerOptions ? . openai ? . itemId ) . toBeUndefined ( )
2026-01-15 07:31:50 +00:00
} )
2026-06-08 22:43:48 +00:00
test ( "uses the SDK package namespace rather than provider ID" , ( ) = > {
2026-01-15 07:31:50 +00:00
const zenModel = {
. . . openaiModel ,
providerID : "zen" ,
}
const msgs = [
{
role : "assistant" ,
content : [
{
type : "reasoning" ,
text : "thinking..." ,
providerOptions : {
openai : {
itemId : "rs_123" ,
reasoningEncryptedContent : "encrypted" ,
} ,
} ,
} ,
{
type : "text" ,
text : "Hello" ,
providerOptions : {
openai : {
itemId : "msg_456" ,
} ,
} ,
} ,
] ,
} ,
] as any [ ]
const result = ProviderTransform . message ( msgs , zenModel , { store : false } ) as any [ ]
expect ( result ) . toHaveLength ( 1 )
2026-06-08 22:43:48 +00:00
expect ( result [ 0 ] . content [ 0 ] . providerOptions ? . openai ? . itemId ) . toBeUndefined ( )
expect ( result [ 0 ] . content [ 0 ] . providerOptions ? . openai ? . reasoningEncryptedContent ) . toBe ( "encrypted" )
expect ( result [ 0 ] . content [ 1 ] . providerOptions ? . openai ? . itemId ) . toBeUndefined ( )
2026-01-15 07:31:50 +00:00
} )
2026-06-08 22:43:48 +00:00
test ( "preserves other OpenAI options" , ( ) = > {
2026-01-15 07:31:50 +00:00
const msgs = [
{
role : "assistant" ,
content : [
{
type : "text" ,
text : "Hello" ,
providerOptions : {
openai : {
itemId : "msg_123" ,
otherOption : "value" ,
} ,
} ,
} ,
] ,
} ,
] as any [ ]
const result = ProviderTransform . message ( msgs , openaiModel , { store : false } ) as any [ ]
2026-06-08 22:43:48 +00:00
expect ( result [ 0 ] . content [ 0 ] . providerOptions ? . openai ? . itemId ) . toBeUndefined ( )
2026-01-15 07:31:50 +00:00
expect ( result [ 0 ] . content [ 0 ] . providerOptions ? . openai ? . otherOption ) . toBe ( "value" )
} )
2026-06-08 22:43:48 +00:00
test ( "strips Azure itemId from the Azure namespace" , ( ) = > {
const azureModel = {
. . . openaiModel ,
providerID : "azure" ,
api : {
id : "gpt-5" ,
url : "https://example.openai.azure.com" ,
npm : "@ai-sdk/azure" ,
} ,
}
const msgs = [
{
role : "assistant" ,
content : [
{
type : "text" ,
text : "Hello" ,
providerOptions : {
azure : { itemId : "msg_123" , otherOption : "value" } ,
openai : { itemId : "msg_openai" } ,
} ,
} ,
] ,
} ,
] as any [ ]
const result = ProviderTransform . message ( msgs , azureModel , { store : false } ) as any [ ]
expect ( result [ 0 ] . content [ 0 ] . providerOptions ? . azure ? . itemId ) . toBeUndefined ( )
expect ( result [ 0 ] . content [ 0 ] . providerOptions ? . azure ? . otherOption ) . toBe ( "value" )
expect ( result [ 0 ] . content [ 0 ] . providerOptions ? . openai ? . itemId ) . toBe ( "msg_openai" )
} )
test ( "strips Bedrock Mantle itemId from the OpenAI namespace" , ( ) = > {
const mantleModel = {
. . . openaiModel ,
providerID : "amazon-bedrock" ,
api : {
id : "openai.gpt-5.5" ,
url : "https://bedrock-mantle.us-east-2.api.aws/openai/v1" ,
npm : "@ai-sdk/amazon-bedrock/mantle" ,
} ,
}
const msgs = [
{
role : "assistant" ,
providerOptions : { openai : { itemId : "msg_root" , otherOption : "root-value" } } ,
content : [
{
type : "reasoning" ,
text : "thinking..." ,
providerOptions : {
openai : { itemId : "rs_123" , reasoningEncryptedContent : "encrypted" } ,
} ,
} ,
] ,
} ,
] as any [ ]
const result = ProviderTransform . message ( msgs , mantleModel , { store : false } ) as any [ ]
expect ( result [ 0 ] . providerOptions ? . openai ? . itemId ) . toBeUndefined ( )
expect ( result [ 0 ] . providerOptions ? . openai ? . otherOption ) . toBe ( "root-value" )
expect ( result [ 0 ] . content [ 0 ] . providerOptions ? . openai ? . itemId ) . toBeUndefined ( )
expect ( result [ 0 ] . content [ 0 ] . providerOptions ? . openai ? . reasoningEncryptedContent ) . toBe ( "encrypted" )
} )
2026-01-17 04:09:36 +00:00
test ( "preserves metadata for openai package when store is true" , ( ) = > {
2026-01-15 07:31:50 +00:00
const msgs = [
{
role : "assistant" ,
content : [
{
type : "text" ,
text : "Hello" ,
providerOptions : {
openai : {
itemId : "msg_123" ,
} ,
} ,
} ,
] ,
} ,
] as any [ ]
2026-01-17 04:09:36 +00:00
// openai package preserves itemId regardless of store value
2026-01-15 07:31:50 +00:00
const result = ProviderTransform . message ( msgs , openaiModel , { store : true } ) as any [ ]
2026-01-17 04:09:36 +00:00
expect ( result [ 0 ] . content [ 0 ] . providerOptions ? . openai ? . itemId ) . toBe ( "msg_123" )
2026-01-15 07:31:50 +00:00
} )
2026-01-17 04:09:36 +00:00
test ( "preserves metadata for non-openai packages when store is false" , ( ) = > {
2026-01-15 07:31:50 +00:00
const anthropicModel = {
. . . openaiModel ,
providerID : "anthropic" ,
api : {
id : "claude-3" ,
url : "https://api.anthropic.com" ,
npm : "@ai-sdk/anthropic" ,
} ,
}
const msgs = [
{
role : "assistant" ,
content : [
{
type : "text" ,
text : "Hello" ,
providerOptions : {
openai : {
itemId : "msg_123" ,
} ,
} ,
} ,
] ,
} ,
] as any [ ]
2026-01-17 04:09:36 +00:00
// store=false preserves metadata for non-openai packages
2026-01-15 07:31:50 +00:00
const result = ProviderTransform . message ( msgs , anthropicModel , { store : false } ) as any [ ]
2026-01-17 04:09:36 +00:00
expect ( result [ 0 ] . content [ 0 ] . providerOptions ? . openai ? . itemId ) . toBe ( "msg_123" )
2026-01-15 07:31:50 +00:00
} )
2026-01-17 04:09:36 +00:00
test ( "preserves metadata using providerID key when store is false" , ( ) = > {
2026-01-16 17:32:12 +00:00
const opencodeModel = {
. . . openaiModel ,
providerID : "opencode" ,
api : {
id : "opencode-test" ,
url : "https://api.opencode.ai" ,
npm : "@ai-sdk/openai-compatible" ,
} ,
}
const msgs = [
{
role : "assistant" ,
content : [
{
type : "text" ,
text : "Hello" ,
providerOptions : {
opencode : {
itemId : "msg_123" ,
otherOption : "value" ,
} ,
} ,
} ,
] ,
} ,
] as any [ ]
const result = ProviderTransform . message ( msgs , opencodeModel , { store : false } ) as any [ ]
2026-01-17 04:09:36 +00:00
expect ( result [ 0 ] . content [ 0 ] . providerOptions ? . opencode ? . itemId ) . toBe ( "msg_123" )
2026-01-16 17:32:12 +00:00
expect ( result [ 0 ] . content [ 0 ] . providerOptions ? . opencode ? . otherOption ) . toBe ( "value" )
} )
2026-01-17 04:09:36 +00:00
test ( "preserves itemId across all providerOptions keys" , ( ) = > {
2026-01-16 17:32:12 +00:00
const opencodeModel = {
. . . openaiModel ,
providerID : "opencode" ,
api : {
id : "opencode-test" ,
url : "https://api.opencode.ai" ,
npm : "@ai-sdk/openai-compatible" ,
} ,
}
const msgs = [
{
role : "assistant" ,
providerOptions : {
openai : { itemId : "msg_root" } ,
opencode : { itemId : "msg_opencode" } ,
extra : { itemId : "msg_extra" } ,
} ,
content : [
{
type : "text" ,
text : "Hello" ,
providerOptions : {
openai : { itemId : "msg_openai_part" } ,
opencode : { itemId : "msg_opencode_part" } ,
extra : { itemId : "msg_extra_part" } ,
} ,
} ,
] ,
} ,
] as any [ ]
const result = ProviderTransform . message ( msgs , opencodeModel , { store : false } ) as any [ ]
2026-01-17 04:09:36 +00:00
expect ( result [ 0 ] . providerOptions ? . openai ? . itemId ) . toBe ( "msg_root" )
expect ( result [ 0 ] . providerOptions ? . opencode ? . itemId ) . toBe ( "msg_opencode" )
expect ( result [ 0 ] . providerOptions ? . extra ? . itemId ) . toBe ( "msg_extra" )
expect ( result [ 0 ] . content [ 0 ] . providerOptions ? . openai ? . itemId ) . toBe ( "msg_openai_part" )
expect ( result [ 0 ] . content [ 0 ] . providerOptions ? . opencode ? . itemId ) . toBe ( "msg_opencode_part" )
expect ( result [ 0 ] . content [ 0 ] . providerOptions ? . extra ? . itemId ) . toBe ( "msg_extra_part" )
2026-01-16 17:32:12 +00:00
} )
2026-01-15 07:31:50 +00:00
test ( "does not strip metadata for non-openai packages when store is not false" , ( ) = > {
const anthropicModel = {
. . . openaiModel ,
providerID : "anthropic" ,
api : {
id : "claude-3" ,
url : "https://api.anthropic.com" ,
npm : "@ai-sdk/anthropic" ,
} ,
}
const msgs = [
{
role : "assistant" ,
content : [
{
type : "text" ,
text : "Hello" ,
providerOptions : {
openai : {
itemId : "msg_123" ,
} ,
} ,
} ,
] ,
} ,
] as any [ ]
const result = ProviderTransform . message ( msgs , anthropicModel , { } ) as any [ ]
expect ( result [ 0 ] . content [ 0 ] . providerOptions ? . openai ? . itemId ) . toBe ( "msg_123" )
} )
} )
2026-01-17 06:35:49 +00:00
describe ( "ProviderTransform.message - providerOptions key remapping" , ( ) = > {
const createModel = ( providerID : string , npm : string ) = >
( {
id : ` ${ providerID } /test-model ` ,
providerID ,
api : {
id : "test-model" ,
url : "https://api.test.com" ,
npm ,
} ,
name : "Test Model" ,
capabilities : {
temperature : true ,
reasoning : false ,
attachment : true ,
toolcall : true ,
input : { text : true , audio : false , image : true , video : false , pdf : true } ,
output : { text : true , audio : false , image : false , video : false , pdf : false } ,
interleaved : false ,
} ,
cost : { input : 0.001 , output : 0.002 , cache : { read : 0.0001 , write : 0.0002 } } ,
limit : { context : 128000 , output : 8192 } ,
status : "active" ,
options : { } ,
headers : { } ,
} ) as any
test ( "azure keeps 'azure' key and does not remap to 'openai'" , ( ) = > {
const model = createModel ( "azure" , "@ai-sdk/azure" )
const msgs = [
{
role : "user" ,
content : "Hello" ,
providerOptions : {
azure : { someOption : "value" } ,
} ,
} ,
] as any [ ]
const result = ProviderTransform . message ( msgs , model , { } )
expect ( result [ 0 ] . providerOptions ? . azure ) . toEqual ( { someOption : "value" } )
expect ( result [ 0 ] . providerOptions ? . openai ) . toBeUndefined ( )
} )
2026-03-31 19:57:15 +00:00
test ( "azure cognitive services remaps providerID to 'azure' key" , ( ) = > {
const model = createModel ( "azure-cognitive-services" , "@ai-sdk/azure" )
const msgs = [
{
role : "user" ,
content : [
{
type : "text" ,
text : "Hello" ,
providerOptions : {
"azure-cognitive-services" : { part : true } ,
} ,
} ,
] ,
providerOptions : {
"azure-cognitive-services" : { someOption : "value" } ,
} ,
} ,
] as any [ ]
const result = ProviderTransform . message ( msgs , model , { } ) as any [ ]
const part = result [ 0 ] . content [ 0 ] as any
expect ( result [ 0 ] . providerOptions ? . azure ) . toEqual ( { someOption : "value" } )
expect ( result [ 0 ] . providerOptions ? . [ "azure-cognitive-services" ] ) . toBeUndefined ( )
expect ( part . providerOptions ? . azure ) . toEqual ( { part : true } )
expect ( part . providerOptions ? . [ "azure-cognitive-services" ] ) . toBeUndefined ( )
} )
2026-01-31 01:53:22 +00:00
test ( "copilot remaps providerID to 'copilot' key" , ( ) = > {
2026-01-17 06:35:49 +00:00
const model = createModel ( "github-copilot" , "@ai-sdk/github-copilot" )
const msgs = [
{
role : "user" ,
content : "Hello" ,
providerOptions : {
2026-01-31 02:35:22 +00:00
copilot : { someOption : "value" } ,
2026-01-17 06:35:49 +00:00
} ,
} ,
] as any [ ]
const result = ProviderTransform . message ( msgs , model , { } )
2026-01-31 01:53:22 +00:00
expect ( result [ 0 ] . providerOptions ? . copilot ) . toEqual ( { someOption : "value" } )
2026-01-17 06:35:49 +00:00
expect ( result [ 0 ] . providerOptions ? . [ "github-copilot" ] ) . toBeUndefined ( )
} )
test ( "bedrock remaps providerID to 'bedrock' key" , ( ) = > {
const model = createModel ( "my-bedrock" , "@ai-sdk/amazon-bedrock" )
const msgs = [
{
role : "user" ,
content : "Hello" ,
providerOptions : {
"my-bedrock" : { someOption : "value" } ,
} ,
} ,
] as any [ ]
const result = ProviderTransform . message ( msgs , model , { } )
expect ( result [ 0 ] . providerOptions ? . bedrock ) . toEqual ( { someOption : "value" } )
expect ( result [ 0 ] . providerOptions ? . [ "my-bedrock" ] ) . toBeUndefined ( )
} )
} )
2026-01-21 16:42:13 +00:00
describe ( "ProviderTransform.message - claude w/bedrock custom inference profile" , ( ) = > {
test ( "adds cachePoint" , ( ) = > {
const model = {
id : "amazon-bedrock/custom-claude-sonnet-4.5" ,
providerID : "amazon-bedrock" ,
api : {
id : "arn:aws:bedrock:xxx:yyy:application-inference-profile/zzz" ,
url : "https://api.test.com" ,
npm : "@ai-sdk/amazon-bedrock" ,
} ,
name : "Custom inference profile" ,
capabilities : { } ,
options : { } ,
headers : { } ,
} as any
const msgs = [
{
role : "user" ,
content : "Hello" ,
} ,
] as any [ ]
const result = ProviderTransform . message ( msgs , model , { } )
expect ( result [ 0 ] . providerOptions ? . bedrock ) . toEqual (
expect . objectContaining ( {
cachePoint : {
2026-02-01 20:42:42 +00:00
type : "default" ,
2026-01-21 16:42:13 +00:00
} ,
} ) ,
)
} )
} )
2026-03-24 18:42:20 +00:00
describe ( "ProviderTransform.message - bedrock caching with non-bedrock providerID" , ( ) = > {
test ( "applies cache options at message level when npm package is amazon-bedrock" , ( ) = > {
const model = {
id : "aws/us.anthropic.claude-opus-4-6-v1" ,
providerID : "aws" ,
api : {
id : "us.anthropic.claude-opus-4-6-v1" ,
url : "https://bedrock-runtime.us-east-1.amazonaws.com" ,
npm : "@ai-sdk/amazon-bedrock" ,
} ,
name : "Claude Opus 4.6" ,
capabilities : { } ,
options : { } ,
headers : { } ,
} as any
const msgs = [
{
role : "system" ,
2026-05-05 23:07:23 +00:00
content : "You are a helpful assistant" ,
2026-03-24 18:42:20 +00:00
} ,
{
role : "user" ,
content : [ { type : "text" , text : "Hello" } ] ,
} ,
] as any [ ]
const result = ProviderTransform . message ( msgs , model , { } ) as any [ ]
2026-03-24 18:43:20 +00:00
2026-03-24 18:42:20 +00:00
// Cache should be at the message level and not the content-part level
expect ( result [ 0 ] . providerOptions ? . bedrock ) . toEqual ( {
cachePoint : { type : "default" } ,
} )
2026-05-05 23:07:23 +00:00
expect ( result [ 0 ] . content ) . toBe ( "You are a helpful assistant" )
2026-03-24 18:42:20 +00:00
} )
} )
2026-02-14 18:32:29 +00:00
describe ( "ProviderTransform.message - cache control on gateway" , ( ) = > {
const createModel = ( overrides : Partial < any > = { } ) = >
( {
id : "anthropic/claude-sonnet-4" ,
providerID : "vercel" ,
api : {
id : "anthropic/claude-sonnet-4" ,
url : "https://ai-gateway.vercel.sh/v3/ai" ,
npm : "@ai-sdk/gateway" ,
} ,
name : "Claude Sonnet 4" ,
capabilities : {
temperature : true ,
reasoning : true ,
attachment : true ,
toolcall : true ,
input : { text : true , audio : false , image : true , video : false , pdf : true } ,
output : { text : true , audio : false , image : false , video : false , pdf : false } ,
interleaved : false ,
} ,
cost : { input : 0.001 , output : 0.002 , cache : { read : 0.0001 , write : 0.0002 } } ,
limit : { context : 200_000 , output : 8192 } ,
status : "active" ,
options : { } ,
headers : { } ,
. . . overrides ,
} ) as any
test ( "gateway does not set cache control for anthropic models" , ( ) = > {
const model = createModel ( )
const msgs = [
{
role : "system" ,
2026-05-05 23:07:23 +00:00
content : "You are a helpful assistant" ,
2026-02-14 18:32:29 +00:00
} ,
{
role : "user" ,
content : "Hello" ,
} ,
] as any [ ]
const result = ProviderTransform . message ( msgs , model , { } ) as any [ ]
2026-05-05 23:07:23 +00:00
expect ( result [ 0 ] . content ) . toBe ( "You are a helpful assistant" )
2026-02-14 18:32:29 +00:00
expect ( result [ 0 ] . providerOptions ) . toBeUndefined ( )
} )
test ( "non-gateway anthropic keeps existing cache control behavior" , ( ) = > {
const model = createModel ( {
providerID : "anthropic" ,
api : {
id : "claude-sonnet-4" ,
url : "https://api.anthropic.com" ,
npm : "@ai-sdk/anthropic" ,
} ,
} )
const msgs = [
{
role : "system" ,
content : "You are a helpful assistant" ,
} ,
{
role : "user" ,
content : "Hello" ,
} ,
] as any [ ]
const result = ProviderTransform . message ( msgs , model , { } ) as any [ ]
2026-03-31 20:16:14 +00:00
expect ( result [ 0 ] . providerOptions ) . toEqual ( {
anthropic : {
cacheControl : {
type : "ephemeral" ,
} ,
} ,
openrouter : {
cacheControl : {
type : "ephemeral" ,
} ,
} ,
bedrock : {
cachePoint : {
type : "default" ,
} ,
} ,
openaiCompatible : {
cache_control : {
type : "ephemeral" ,
} ,
} ,
copilot : {
copilot_cache_control : {
type : "ephemeral" ,
} ,
} ,
2026-04-13 13:17:13 +00:00
alibaba : {
cacheControl : {
type : "ephemeral" ,
} ,
} ,
2026-03-31 20:16:14 +00:00
} )
} )
test ( "google-vertex-anthropic applies cache control" , ( ) = > {
const model = createModel ( {
providerID : "google-vertex-anthropic" ,
api : {
id : "google-vertex-anthropic" ,
url : "https://us-central1-aiplatform.googleapis.com" ,
npm : "@ai-sdk/google-vertex/anthropic" ,
} ,
id : "claude-sonnet-4@20250514" ,
} )
const msgs = [
{
role : "system" ,
content : "You are a helpful assistant" ,
} ,
{
role : "user" ,
content : "Hello" ,
} ,
] as any [ ]
const result = ProviderTransform . message ( msgs , model , { } ) as any [ ]
2026-02-14 18:32:29 +00:00
expect ( result [ 0 ] . providerOptions ) . toEqual ( {
anthropic : {
cacheControl : {
type : "ephemeral" ,
} ,
} ,
openrouter : {
cacheControl : {
type : "ephemeral" ,
} ,
} ,
bedrock : {
cachePoint : {
type : "default" ,
} ,
} ,
openaiCompatible : {
cache_control : {
type : "ephemeral" ,
} ,
} ,
copilot : {
copilot_cache_control : {
type : "ephemeral" ,
} ,
} ,
2026-04-13 13:17:13 +00:00
alibaba : {
cacheControl : {
type : "ephemeral" ,
} ,
} ,
2026-02-14 18:32:29 +00:00
} )
} )
} )
2026-06-09 15:54:03 +00:00
describe ( "ProviderTransform.temperature - Cohere North" , ( ) = > {
test ( "defaults north-mini-code models to 1.0" , ( ) = > {
expect ( ProviderTransform . temperature ( { id : "cohere/North-Mini-Code-1-0-latest" } as any ) ) . toBe ( 1.0 )
} )
} )
2025-12-30 22:37:32 +00:00
describe ( "ProviderTransform.variants" , ( ) = > {
const createMockModel = ( overrides : Partial < any > = { } ) : any = > ( {
id : "test/test-model" ,
providerID : "test" ,
api : {
id : "test-model" ,
url : "https://api.test.com" ,
npm : "@ai-sdk/openai" ,
} ,
name : "Test Model" ,
capabilities : {
temperature : true ,
reasoning : true ,
attachment : true ,
toolcall : true ,
input : { text : true , audio : false , image : true , video : false , pdf : false } ,
output : { text : true , audio : false , image : false , video : false , pdf : false } ,
interleaved : false ,
} ,
cost : {
input : 0.001 ,
output : 0.002 ,
cache : { read : 0.0001 , write : 0.0002 } ,
} ,
limit : {
2026-01-29 04:02:07 +00:00
context : 200_000 ,
output : 64_000 ,
2025-12-30 22:37:32 +00:00
} ,
status : "active" ,
options : { } ,
headers : { } ,
release_date : "2024-01-01" ,
. . . overrides ,
} )
test ( "returns empty object when model has no reasoning capabilities" , ( ) = > {
const model = createMockModel ( {
capabilities : { reasoning : false } ,
} )
const result = ProviderTransform . variants ( model )
expect ( result ) . toEqual ( { } )
} )
test ( "deepseek returns empty object" , ( ) = > {
const model = createMockModel ( {
id : "deepseek/deepseek-chat" ,
providerID : "deepseek" ,
api : {
id : "deepseek-chat" ,
url : "https://api.deepseek.com" ,
npm : "@ai-sdk/openai-compatible" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( result ) . toEqual ( { } )
} )
test ( "minimax returns empty object" , ( ) = > {
const model = createMockModel ( {
id : "minimax/minimax-model" ,
providerID : "minimax" ,
api : {
id : "minimax-model" ,
url : "https://api.minimax.com" ,
npm : "@ai-sdk/openai-compatible" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( result ) . toEqual ( { } )
} )
2026-06-08 22:03:31 +00:00
test ( "minimax m3 using anthropic returns thinking toggles" , ( ) = > {
const model = createMockModel ( {
id : "minimax/minimax-m3" ,
providerID : "minimax" ,
api : {
id : "MiniMax-M3" ,
url : "https://api.minimax.com/anthropic/v1" ,
npm : "@ai-sdk/anthropic" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( result ) . toEqual ( {
none : { thinking : { type : "disabled" } } ,
thinking : { thinking : { type : "adaptive" } } ,
} )
} )
test ( "minimax m3 using openai-compatible returns thinking toggles" , ( ) = > {
const model = createMockModel ( {
id : "minimax/minimax-m3" ,
providerID : "minimax" ,
api : {
id : "minimax-m3" ,
url : "https://api.minimax.com/v1" ,
npm : "@ai-sdk/openai-compatible" ,
} ,
} )
expect ( ProviderTransform . variants ( model ) ) . toEqual ( {
none : { thinking : { type : "disabled" } } ,
thinking : { thinking : { type : "adaptive" } } ,
} )
} )
2025-12-30 22:37:32 +00:00
test ( "glm returns empty object" , ( ) = > {
const model = createMockModel ( {
id : "glm/glm-4" ,
providerID : "glm" ,
api : {
id : "glm-4" ,
url : "https://api.glm.com" ,
npm : "@ai-sdk/openai-compatible" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( result ) . toEqual ( { } )
} )
2026-06-20 21:59:08 +00:00
test ( "glm-5.2 returns native effort variants for openai-compatible providers" , ( ) = > {
const model = createMockModel ( {
id : "zhipuai/glm-5.2" ,
providerID : "zhipuai" ,
api : {
id : "glm-5.2" ,
url : "https://open.bigmodel.cn/api/paas/v4" ,
npm : "@ai-sdk/openai-compatible" ,
} ,
} )
expect ( ProviderTransform . variants ( model ) ) . toEqual ( {
high : { reasoningEffort : "high" } ,
max : { reasoningEffort : "max" } ,
} )
} )
test ( "recognizes GLM-5.2 provider model IDs" , ( ) = > {
for ( const id of [ "accounts/fireworks/models/glm-5p2" , "zai-org-glm-5-2" , "umans-glm-5.2" ] ) {
const model = createMockModel ( {
id : ` test/ ${ id } ` ,
api : {
id ,
url : "https://api.test.com" ,
npm : "@ai-sdk/openai-compatible" ,
} ,
} )
expect ( ProviderTransform . variants ( model ) ) . toEqual ( {
high : { reasoningEffort : "high" } ,
max : { reasoningEffort : "max" } ,
} )
}
} )
test ( "recognizes GLM-5.2 from the API ID when the configured model ID is an alias" , ( ) = > {
const model = createMockModel ( {
id : "custom/my-glm" ,
api : {
id : "accounts/fireworks/models/glm-5p2" ,
url : "https://api.fireworks.ai/inference/v1" ,
npm : "@ai-sdk/openai-compatible" ,
} ,
} )
expect ( ProviderTransform . variants ( model ) ) . toEqual ( {
high : { reasoningEffort : "high" } ,
max : { reasoningEffort : "max" } ,
} )
} )
test ( "glm-5.2 returns openrouter effort variants for openrouter" , ( ) = > {
const model = createMockModel ( {
id : "openrouter/z-ai/glm-5.2" ,
providerID : "openrouter" ,
api : {
id : "z-ai/glm-5.2" ,
url : "https://openrouter.ai/api/v1" ,
npm : "@openrouter/ai-sdk-provider" ,
} ,
} )
expect ( ProviderTransform . variants ( model ) ) . toEqual ( {
high : { reasoning : { effort : "high" } } ,
xhigh : { reasoning : { effort : "xhigh" } } ,
} )
} )
test ( "glm-5.2 returns effort variants for anthropic-compatible providers" , ( ) = > {
const model = createMockModel ( {
id : "zai-coding-plan/glm-5.2" ,
providerID : "zai-coding-plan" ,
api : {
id : "glm-5.2" ,
url : "https://api.z.ai/api/anthropic" ,
npm : "@ai-sdk/anthropic" ,
} ,
} )
expect ( ProviderTransform . variants ( model ) ) . toEqual ( {
high : { effort : "high" } ,
max : { effort : "max" } ,
} )
} )
test ( "glm-5.2 falls back to provider defaults for other packages" , ( ) = > {
const model = createMockModel ( {
id : "test/glm-5.2" ,
api : {
id : "glm-5.2" ,
url : "https://api.test.com" ,
npm : "@ai-sdk/amazon-bedrock" ,
} ,
} )
expect ( ProviderTransform . variants ( model ) ) . toEqual ( {
low : { reasoningConfig : { type : "enabled" , maxReasoningEffort : "low" } } ,
medium : { reasoningConfig : { type : "enabled" , maxReasoningEffort : "medium" } } ,
high : { reasoningConfig : { type : "enabled" , maxReasoningEffort : "high" } } ,
} )
} )
2026-04-29 21:26:24 +00:00
test ( "mistral models with reasoning support return variants" , ( ) = > {
2026-04-21 20:45:06 +00:00
const model = createMockModel ( {
id : "mistral/mistral-small-latest" ,
providerID : "mistral" ,
api : {
id : "mistral-small-latest" ,
url : "https://api.mistral.com" ,
npm : "@ai-sdk/mistral" ,
} ,
capabilities : { reasoning : true } ,
} )
const result = ProviderTransform . variants ( model )
expect ( result ) . toEqual ( {
high : { reasoningEffort : "high" } ,
} )
} )
2026-04-29 21:26:24 +00:00
test ( "mistral-medium-3.5 with reasoning returns variants" , ( ) = > {
const model = createMockModel ( {
id : "mistral/mistral-medium-3.5" ,
providerID : "mistral" ,
api : {
id : "mistral-medium-3.5" ,
url : "https://api.mistral.com" ,
npm : "@ai-sdk/mistral" ,
} ,
capabilities : { reasoning : true } ,
} )
const result = ProviderTransform . variants ( model )
expect ( result ) . toEqual ( {
high : { reasoningEffort : "high" } ,
} )
} )
2026-04-21 20:45:06 +00:00
test ( "mistral without reasoning returns empty object" , ( ) = > {
const model = createMockModel ( {
id : "mistral/mistral-large" ,
providerID : "mistral" ,
api : {
id : "mistral-large-latest" ,
url : "https://api.mistral.com" ,
npm : "@ai-sdk/mistral" ,
} ,
capabilities : { reasoning : false } ,
} )
const result = ProviderTransform . variants ( model )
expect ( result ) . toEqual ( { } )
} )
test ( "mistral large with reasoning returns empty object (only small supports reasoning)" , ( ) = > {
2025-12-30 22:37:32 +00:00
const model = createMockModel ( {
id : "mistral/mistral-large" ,
providerID : "mistral" ,
api : {
id : "mistral-large-latest" ,
url : "https://api.mistral.com" ,
npm : "@ai-sdk/mistral" ,
} ,
2026-04-21 20:45:06 +00:00
capabilities : { reasoning : true } ,
2025-12-30 22:37:32 +00:00
} )
const result = ProviderTransform . variants ( model )
expect ( result ) . toEqual ( { } )
} )
describe ( "@openrouter/ai-sdk-provider" , ( ) = > {
2026-06-09 01:22:02 +00:00
test ( "returns widely supported efforts for other reasoning models" , ( ) = > {
2025-12-30 22:37:32 +00:00
const model = createMockModel ( {
id : "openrouter/test-model" ,
providerID : "openrouter" ,
api : {
id : "test-model" ,
url : "https://openrouter.ai" ,
npm : "@openrouter/ai-sdk-provider" ,
} ,
} )
const result = ProviderTransform . variants ( model )
2026-06-09 01:22:02 +00:00
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" ] )
expect ( result . medium ) . toEqual ( { reasoning : { effort : "medium" } } )
2025-12-30 22:37:32 +00:00
} )
test ( "gpt models return OPENAI_EFFORTS with reasoning" , ( ) = > {
const model = createMockModel ( {
id : "openrouter/gpt-4" ,
providerID : "openrouter" ,
api : {
id : "gpt-4" ,
url : "https://openrouter.ai" ,
npm : "@openrouter/ai-sdk-provider" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "none" , "minimal" , "low" , "medium" , "high" , "xhigh" ] )
expect ( result . low ) . toEqual ( { reasoning : { effort : "low" } } )
expect ( result . high ) . toEqual ( { reasoning : { effort : "high" } } )
} )
2026-05-08 03:32:47 +00:00
for ( const testCase of [
2026-06-09 01:22:02 +00:00
{ id : "openai/o3-mini" , efforts : [ "none" , "minimal" , "low" , "medium" , "high" , "xhigh" ] } ,
2026-05-08 03:32:47 +00:00
{ id : "openai/gpt-5.4" , efforts : [ "none" , "low" , "medium" , "high" , "xhigh" ] } ,
{ id : "openai/gpt-5-pro" , efforts : [ "high" ] } ,
{ id : "openai/gpt-5.5-pro" , efforts : [ "medium" , "high" , "xhigh" ] } ,
{ id : "openai/gpt-5.2-codex" , efforts : [ "low" , "medium" , "high" , "xhigh" ] } ,
{ id : "openai/gpt-5.3-codex" , efforts : [ "none" , "low" , "medium" , "high" , "xhigh" ] } ,
{ id : "openai/gpt-5.3-codex-max" , efforts : [ "none" , "low" , "medium" , "high" , "xhigh" ] } ,
{ id : "openai/gpt-5-chat-latest" , efforts : [ ] } ,
{ id : "openai/gpt-5.2-chat-latest" , efforts : [ "medium" ] } ,
] ) {
test ( ` ${ testCase . id } returns supported OpenAI reasoning efforts ` , ( ) = > {
const result = ProviderTransform . variants (
createMockModel ( {
id : testCase.id ,
providerID : "openrouter" ,
api : {
id : testCase.id ,
url : "https://openrouter.ai" ,
npm : "@openrouter/ai-sdk-provider" ,
} ,
} ) ,
)
expect ( Object . keys ( result ) ) . toEqual ( testCase . efforts )
} )
}
2026-06-09 01:22:02 +00:00
test ( "gemini-3 returns widely supported efforts with reasoning" , ( ) = > {
2025-12-30 22:37:32 +00:00
const model = createMockModel ( {
id : "openrouter/gemini-3-5-pro" ,
providerID : "openrouter" ,
api : {
id : "gemini-3-5-pro" ,
url : "https://openrouter.ai" ,
npm : "@openrouter/ai-sdk-provider" ,
} ,
} )
const result = ProviderTransform . variants ( model )
2026-06-09 01:22:02 +00:00
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" ] )
2025-12-30 22:37:32 +00:00
} )
2026-01-18 20:44:39 +00:00
test ( "grok-4 returns empty object" , ( ) = > {
2025-12-30 22:37:32 +00:00
const model = createMockModel ( {
id : "openrouter/grok-4" ,
providerID : "openrouter" ,
api : {
id : "grok-4" ,
url : "https://openrouter.ai" ,
npm : "@openrouter/ai-sdk-provider" ,
} ,
} )
const result = ProviderTransform . variants ( model )
2026-01-18 20:44:39 +00:00
expect ( result ) . toEqual ( { } )
} )
test ( "grok-3-mini returns low and high with reasoning" , ( ) = > {
const model = createMockModel ( {
id : "openrouter/grok-3-mini" ,
providerID : "openrouter" ,
api : {
id : "grok-3-mini" ,
url : "https://openrouter.ai" ,
npm : "@openrouter/ai-sdk-provider" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "high" ] )
expect ( result . low ) . toEqual ( { reasoning : { effort : "low" } } )
expect ( result . high ) . toEqual ( { reasoning : { effort : "high" } } )
2025-12-30 22:37:32 +00:00
} )
} )
describe ( "@ai-sdk/gateway" , ( ) = > {
2026-02-19 22:17:57 +00:00
test ( "anthropic sonnet 4.6 models return adaptive thinking options" , ( ) = > {
const model = createMockModel ( {
id : "anthropic/claude-sonnet-4-6" ,
providerID : "gateway" ,
api : {
id : "anthropic/claude-sonnet-4-6" ,
url : "https://gateway.ai" ,
npm : "@ai-sdk/gateway" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" , "max" ] )
expect ( result . medium ) . toEqual ( {
thinking : {
type : "adaptive" ,
} ,
effort : "medium" ,
} )
} )
test ( "anthropic sonnet 4.6 dot-format models return adaptive thinking options" , ( ) = > {
const model = createMockModel ( {
id : "anthropic/claude-sonnet-4-6" ,
providerID : "gateway" ,
api : {
id : "anthropic/claude-sonnet-4.6" ,
url : "https://gateway.ai" ,
npm : "@ai-sdk/gateway" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" , "max" ] )
expect ( result . medium ) . toEqual ( {
thinking : {
type : "adaptive" ,
} ,
effort : "medium" ,
} )
} )
test ( "anthropic opus 4.6 dot-format models return adaptive thinking options" , ( ) = > {
const model = createMockModel ( {
id : "anthropic/claude-opus-4-6" ,
providerID : "gateway" ,
api : {
id : "anthropic/claude-opus-4.6" ,
url : "https://gateway.ai" ,
npm : "@ai-sdk/gateway" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" , "max" ] )
expect ( result . high ) . toEqual ( {
thinking : {
type : "adaptive" ,
} ,
effort : "high" ,
} )
} )
2026-04-16 15:57:36 +00:00
test ( "anthropic opus 4.7 models return adaptive thinking options with xhigh" , ( ) = > {
const model = createMockModel ( {
id : "anthropic/claude-opus-4-7" ,
providerID : "gateway" ,
api : {
id : "anthropic/claude-opus-4-7" ,
url : "https://gateway.ai" ,
npm : "@ai-sdk/gateway" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" , "xhigh" , "max" ] )
expect ( result . xhigh ) . toEqual ( {
thinking : {
type : "adaptive" ,
2026-05-30 23:07:06 +00:00
display : "summarized" ,
2026-04-16 15:57:36 +00:00
} ,
effort : "xhigh" ,
} )
expect ( result . max ) . toEqual ( {
thinking : {
type : "adaptive" ,
2026-05-30 23:07:06 +00:00
display : "summarized" ,
2026-04-16 15:57:36 +00:00
} ,
effort : "max" ,
} )
} )
test ( "anthropic opus 4.7 dot-format models return adaptive thinking options with xhigh" , ( ) = > {
const model = createMockModel ( {
id : "anthropic/claude-opus-4-7" ,
providerID : "gateway" ,
api : {
id : "anthropic/claude-opus-4.7" ,
url : "https://gateway.ai" ,
npm : "@ai-sdk/gateway" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" , "xhigh" , "max" ] )
} )
2026-05-30 23:07:06 +00:00
test ( "anthropic opus 4.8 forces display summarized for adaptive reasoning" , ( ) = > {
const model = createMockModel ( {
id : "anthropic/claude-opus-4-8" ,
providerID : "gateway" ,
api : {
id : "anthropic/claude-opus-4-8" ,
url : "https://gateway.ai" ,
npm : "@ai-sdk/gateway" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" , "xhigh" , "max" ] )
expect ( result . high ) . toEqual ( {
thinking : {
type : "adaptive" ,
display : "summarized" ,
} ,
effort : "high" ,
} )
} )
test ( "anthropic opus 4.6 omits display so it keeps the summarized default" , ( ) = > {
const model = createMockModel ( {
id : "anthropic/claude-opus-4-6" ,
providerID : "gateway" ,
api : {
id : "anthropic/claude-opus-4-6" ,
url : "https://gateway.ai" ,
npm : "@ai-sdk/gateway" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" , "max" ] )
expect ( result . high ) . toEqual ( {
thinking : {
type : "adaptive" ,
} ,
effort : "high" ,
} )
} )
2026-02-14 18:32:29 +00:00
test ( "anthropic models return anthropic thinking options" , ( ) = > {
const model = createMockModel ( {
id : "anthropic/claude-sonnet-4" ,
providerID : "gateway" ,
api : {
id : "anthropic/claude-sonnet-4" ,
url : "https://gateway.ai" ,
npm : "@ai-sdk/gateway" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "high" , "max" ] )
expect ( result . high ) . toEqual ( {
thinking : {
type : "enabled" ,
budgetTokens : 16000 ,
} ,
} )
expect ( result . max ) . toEqual ( {
thinking : {
type : "enabled" ,
budgetTokens : 31999 ,
} ,
} )
} )
2025-12-30 22:37:32 +00:00
test ( "returns OPENAI_EFFORTS with reasoningEffort" , ( ) = > {
const model = createMockModel ( {
id : "gateway/gateway-model" ,
providerID : "gateway" ,
api : {
id : "gateway-model" ,
url : "https://gateway.ai" ,
npm : "@ai-sdk/gateway" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "none" , "minimal" , "low" , "medium" , "high" , "xhigh" ] )
expect ( result . low ) . toEqual ( { reasoningEffort : "low" } )
expect ( result . high ) . toEqual ( { reasoningEffort : "high" } )
} )
2026-05-08 03:32:47 +00:00
for ( const testCase of [
{ id : "openai/gpt-5-5" , efforts : [ "none" , "low" , "medium" , "high" , "xhigh" ] } ,
{ id : "openai/gpt-5-pro" , efforts : [ "high" ] } ,
{ id : "openai/gpt-5-5-pro" , efforts : [ "medium" , "high" , "xhigh" ] } ,
{ id : "openai/gpt-5-2-codex" , efforts : [ "low" , "medium" , "high" , "xhigh" ] } ,
{ id : "openai/gpt-5-3-codex" , efforts : [ "none" , "low" , "medium" , "high" , "xhigh" ] } ,
{ id : "openai/gpt-5-3-codex-max" , efforts : [ "none" , "low" , "medium" , "high" , "xhigh" ] } ,
{ id : "openai/gpt-5-chat-latest" , efforts : [ ] } ,
{ id : "openai/gpt-5-2-chat-latest" , efforts : [ "medium" ] } ,
] ) {
test ( ` ${ testCase . id } returns supported OpenAI reasoning efforts ` , ( ) = > {
const result = ProviderTransform . variants (
createMockModel ( {
id : testCase.id ,
providerID : "gateway" ,
api : {
id : testCase.id ,
url : "https://gateway.ai" ,
npm : "@ai-sdk/gateway" ,
} ,
} ) ,
)
expect ( Object . keys ( result ) ) . toEqual ( testCase . efforts )
} )
}
2025-12-30 22:37:32 +00:00
} )
2026-01-24 05:09:47 +00:00
describe ( "@ai-sdk/github-copilot" , ( ) = > {
test ( "standard models return low, medium, high" , ( ) = > {
const model = createMockModel ( {
id : "gpt-4.5" ,
providerID : "github-copilot" ,
api : {
id : "gpt-4.5" ,
url : "https://api.githubcopilot.com" ,
npm : "@ai-sdk/github-copilot" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" ] )
expect ( result . low ) . toEqual ( {
reasoningEffort : "low" ,
reasoningSummary : "auto" ,
include : [ "reasoning.encrypted_content" ] ,
} )
} )
test ( "gpt-5.1-codex-max includes xhigh" , ( ) = > {
const model = createMockModel ( {
id : "gpt-5.1-codex-max" ,
providerID : "github-copilot" ,
api : {
id : "gpt-5.1-codex-max" ,
url : "https://api.githubcopilot.com" ,
npm : "@ai-sdk/github-copilot" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" , "xhigh" ] )
} )
test ( "gpt-5.1-codex-mini does not include xhigh" , ( ) = > {
const model = createMockModel ( {
id : "gpt-5.1-codex-mini" ,
providerID : "github-copilot" ,
api : {
id : "gpt-5.1-codex-mini" ,
url : "https://api.githubcopilot.com" ,
npm : "@ai-sdk/github-copilot" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" ] )
} )
test ( "gpt-5.1-codex does not include xhigh" , ( ) = > {
const model = createMockModel ( {
id : "gpt-5.1-codex" ,
providerID : "github-copilot" ,
api : {
id : "gpt-5.1-codex" ,
url : "https://api.githubcopilot.com" ,
npm : "@ai-sdk/github-copilot" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" ] )
} )
test ( "gpt-5.2 includes xhigh" , ( ) = > {
const model = createMockModel ( {
id : "gpt-5.2" ,
providerID : "github-copilot" ,
api : {
id : "gpt-5.2" ,
url : "https://api.githubcopilot.com" ,
npm : "@ai-sdk/github-copilot" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" , "xhigh" ] )
expect ( result . xhigh ) . toEqual ( {
reasoningEffort : "xhigh" ,
reasoningSummary : "auto" ,
include : [ "reasoning.encrypted_content" ] ,
} )
} )
test ( "gpt-5.2-codex includes xhigh" , ( ) = > {
const model = createMockModel ( {
id : "gpt-5.2-codex" ,
providerID : "github-copilot" ,
api : {
id : "gpt-5.2-codex" ,
url : "https://api.githubcopilot.com" ,
npm : "@ai-sdk/github-copilot" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" , "xhigh" ] )
} )
2026-03-09 14:07:12 +00:00
test ( "gpt-5.3-codex includes xhigh" , ( ) = > {
const model = createMockModel ( {
id : "gpt-5.3-codex" ,
providerID : "github-copilot" ,
api : {
id : "gpt-5.3-codex" ,
url : "https://api.githubcopilot.com" ,
npm : "@ai-sdk/github-copilot" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" , "xhigh" ] )
} )
test ( "gpt-5.4 includes xhigh" , ( ) = > {
const model = createMockModel ( {
id : "gpt-5.4" ,
release_date : "2026-03-05" ,
providerID : "github-copilot" ,
api : {
id : "gpt-5.4" ,
url : "https://api.githubcopilot.com" ,
npm : "@ai-sdk/github-copilot" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" , "xhigh" ] )
} )
2026-01-24 05:09:47 +00:00
} )
2025-12-30 22:37:32 +00:00
describe ( "@ai-sdk/cerebras" , ( ) = > {
test ( "returns WIDELY_SUPPORTED_EFFORTS with reasoningEffort" , ( ) = > {
const model = createMockModel ( {
id : "cerebras/llama-4" ,
providerID : "cerebras" ,
api : {
id : "llama-4-sc" ,
url : "https://api.cerebras.ai" ,
npm : "@ai-sdk/cerebras" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" ] )
expect ( result . low ) . toEqual ( { reasoningEffort : "low" } )
expect ( result . high ) . toEqual ( { reasoningEffort : "high" } )
} )
} )
describe ( "@ai-sdk/togetherai" , ( ) = > {
test ( "returns WIDELY_SUPPORTED_EFFORTS with reasoningEffort" , ( ) = > {
const model = createMockModel ( {
id : "togetherai/llama-4" ,
providerID : "togetherai" ,
api : {
id : "llama-4-sc" ,
url : "https://api.togetherai.com" ,
npm : "@ai-sdk/togetherai" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" ] )
expect ( result . low ) . toEqual ( { reasoningEffort : "low" } )
expect ( result . high ) . toEqual ( { reasoningEffort : "high" } )
} )
} )
describe ( "@ai-sdk/xai" , ( ) = > {
2026-01-18 20:44:39 +00:00
test ( "grok-3 returns empty object" , ( ) = > {
2025-12-30 22:37:32 +00:00
const model = createMockModel ( {
id : "xai/grok-3" ,
providerID : "xai" ,
api : {
id : "grok-3" ,
url : "https://api.x.ai" ,
npm : "@ai-sdk/xai" ,
} ,
} )
const result = ProviderTransform . variants ( model )
2026-01-18 20:44:39 +00:00
expect ( result ) . toEqual ( { } )
} )
test ( "grok-3-mini returns low and high with reasoningEffort" , ( ) = > {
const model = createMockModel ( {
id : "xai/grok-3-mini" ,
providerID : "xai" ,
api : {
id : "grok-3-mini" ,
url : "https://api.x.ai" ,
npm : "@ai-sdk/xai" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "high" ] )
2025-12-30 22:37:32 +00:00
expect ( result . low ) . toEqual ( { reasoningEffort : "low" } )
expect ( result . high ) . toEqual ( { reasoningEffort : "high" } )
} )
} )
describe ( "@ai-sdk/deepinfra" , ( ) = > {
test ( "returns WIDELY_SUPPORTED_EFFORTS with reasoningEffort" , ( ) = > {
const model = createMockModel ( {
id : "deepinfra/llama-4" ,
providerID : "deepinfra" ,
api : {
id : "llama-4-sc" ,
url : "https://api.deepinfra.com" ,
npm : "@ai-sdk/deepinfra" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" ] )
expect ( result . low ) . toEqual ( { reasoningEffort : "low" } )
expect ( result . high ) . toEqual ( { reasoningEffort : "high" } )
} )
} )
describe ( "@ai-sdk/openai-compatible" , ( ) = > {
test ( "returns WIDELY_SUPPORTED_EFFORTS with reasoningEffort" , ( ) = > {
const model = createMockModel ( {
id : "custom-provider/custom-model" ,
providerID : "custom-provider" ,
api : {
id : "custom-model" ,
url : "https://api.custom.com" ,
npm : "@ai-sdk/openai-compatible" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" ] )
expect ( result . low ) . toEqual ( { reasoningEffort : "low" } )
expect ( result . high ) . toEqual ( { reasoningEffort : "high" } )
} )
2026-06-09 15:54:03 +00:00
test ( "north-mini-code-1-0 returns only none and high" , ( ) = > {
const model = createMockModel ( {
id : "cohere/north-mini-code-1-0" ,
providerID : "cohere" ,
api : {
id : "North-Mini-Code-1-0-latest" ,
url : "https://api.cohere.com/compatibility/v1" ,
npm : "@ai-sdk/openai-compatible" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( result ) . toEqual ( {
none : { reasoningEffort : "none" } ,
high : { reasoningEffort : "high" } ,
} )
} )
2025-12-30 22:37:32 +00:00
} )
describe ( "@ai-sdk/azure" , ( ) = > {
test ( "o1-mini returns empty object" , ( ) = > {
const model = createMockModel ( {
id : "o1-mini" ,
providerID : "azure" ,
api : {
id : "o1-mini" ,
url : "https://azure.com" ,
npm : "@ai-sdk/azure" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( result ) . toEqual ( { } )
} )
test ( "standard azure models return custom efforts with reasoningSummary" , ( ) = > {
const model = createMockModel ( {
2026-01-02 18:38:44 +00:00
id : "o1" ,
2025-12-30 22:37:32 +00:00
providerID : "azure" ,
api : {
2026-01-02 18:38:44 +00:00
id : "o1" ,
2025-12-30 22:37:32 +00:00
url : "https://azure.com" ,
npm : "@ai-sdk/azure" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" ] )
expect ( result . low ) . toEqual ( {
reasoningEffort : "low" ,
reasoningSummary : "auto" ,
include : [ "reasoning.encrypted_content" ] ,
} )
} )
test ( "gpt-5 adds minimal effort" , ( ) = > {
const model = createMockModel ( {
2026-01-02 18:38:44 +00:00
id : "gpt-5" ,
2025-12-30 22:37:32 +00:00
providerID : "azure" ,
api : {
id : "gpt-5" ,
url : "https://azure.com" ,
npm : "@ai-sdk/azure" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "minimal" , "low" , "medium" , "high" ] )
} )
2026-05-08 03:32:47 +00:00
2026-06-03 20:27:23 +00:00
for ( const testCase of [
{ id : "gpt-5-1" , efforts : [ "none" , "low" , "medium" , "high" ] } ,
{ id : "gpt-5-4" , efforts : [ "none" , "low" , "medium" , "high" , "xhigh" ] } ,
{ id : "gpt-5.4" , efforts : [ "none" , "low" , "medium" , "high" , "xhigh" ] } ,
{ id : "gpt-5-5" , efforts : [ "none" , "low" , "medium" , "high" , "xhigh" ] } ,
] ) {
test ( ` ${ testCase . id } returns supported Azure reasoning efforts ` , ( ) = > {
2026-05-08 03:32:47 +00:00
const result = ProviderTransform . variants (
createMockModel ( {
2026-06-03 20:27:23 +00:00
id : testCase.id ,
2026-05-08 03:32:47 +00:00
providerID : "azure" ,
api : {
2026-06-03 20:27:23 +00:00
id : testCase.id ,
2026-05-08 03:32:47 +00:00
url : "https://azure.com" ,
npm : "@ai-sdk/azure" ,
} ,
} ) ,
)
2026-06-03 20:27:23 +00:00
expect ( Object . keys ( result ) ) . toEqual ( testCase . efforts )
2026-05-08 03:32:47 +00:00
} )
}
2025-12-30 22:37:32 +00:00
} )
describe ( "@ai-sdk/openai" , ( ) = > {
2026-05-08 03:32:47 +00:00
test ( "gpt-5-pro returns only high effort" , ( ) = > {
2025-12-30 22:37:32 +00:00
const model = createMockModel ( {
id : "gpt-5-pro" ,
providerID : "openai" ,
api : {
id : "gpt-5-pro" ,
url : "https://api.openai.com" ,
npm : "@ai-sdk/openai" ,
} ,
} )
const result = ProviderTransform . variants ( model )
2026-05-08 03:32:47 +00:00
expect ( Object . keys ( result ) ) . toEqual ( [ "high" ] )
2025-12-30 22:37:32 +00:00
} )
test ( "standard openai models return custom efforts with reasoningSummary" , ( ) = > {
const model = createMockModel ( {
2026-01-02 18:38:44 +00:00
id : "gpt-5" ,
2025-12-30 22:37:32 +00:00
providerID : "openai" ,
api : {
2026-01-02 18:38:44 +00:00
id : "gpt-5" ,
2025-12-30 22:37:32 +00:00
url : "https://api.openai.com" ,
npm : "@ai-sdk/openai" ,
} ,
release_date : "2024-06-01" ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "minimal" , "low" , "medium" , "high" ] )
expect ( result . low ) . toEqual ( {
reasoningEffort : "low" ,
reasoningSummary : "auto" ,
include : [ "reasoning.encrypted_content" ] ,
} )
} )
test ( "models after 2025-11-13 include 'none' effort" , ( ) = > {
const model = createMockModel ( {
2026-01-02 18:38:44 +00:00
id : "gpt-5-nano" ,
2025-12-30 22:37:32 +00:00
providerID : "openai" ,
api : {
2026-01-02 18:38:44 +00:00
id : "gpt-5-nano" ,
2025-12-30 22:37:32 +00:00
url : "https://api.openai.com" ,
npm : "@ai-sdk/openai" ,
} ,
release_date : "2025-11-14" ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "none" , "minimal" , "low" , "medium" , "high" ] )
} )
test ( "models after 2025-12-04 include 'xhigh' effort" , ( ) = > {
const model = createMockModel ( {
2026-05-08 03:32:47 +00:00
id : "openai/gpt-5-reasoning" ,
2025-12-30 22:37:32 +00:00
providerID : "openai" ,
api : {
2026-05-08 03:32:47 +00:00
id : "gpt-5-reasoning" ,
2025-12-30 22:37:32 +00:00
url : "https://api.openai.com" ,
npm : "@ai-sdk/openai" ,
} ,
release_date : "2025-12-05" ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "none" , "minimal" , "low" , "medium" , "high" , "xhigh" ] )
} )
2026-05-05 21:55:08 +00:00
2026-05-08 03:32:47 +00:00
for ( const testCase of [
2026-05-08 03:43:42 +00:00
{ id : "o1" , releaseDate : "2024-12-17" , efforts : [ "low" , "medium" , "high" ] } ,
{ id : "o1-pro" , releaseDate : "2025-03-19" , efforts : [ "low" , "medium" , "high" ] } ,
{ id : "o3" , releaseDate : "2025-04-16" , efforts : [ "low" , "medium" , "high" ] } ,
{ id : "o3-mini" , releaseDate : "2025-01-31" , efforts : [ "low" , "medium" , "high" ] } ,
{ id : "o3-pro" , releaseDate : "2025-06-10" , efforts : [ "low" , "medium" , "high" ] } ,
{ id : "o4-mini" , releaseDate : "2025-04-16" , efforts : [ "low" , "medium" , "high" ] } ,
{ id : "o3-deep-research" , releaseDate : "2025-06-26" , efforts : [ "medium" ] } ,
{ id : "o4-mini-deep-research" , releaseDate : "2025-06-26" , efforts : [ "medium" ] } ,
2026-05-08 03:32:47 +00:00
{ id : "gpt-5.1" , releaseDate : "2025-11-13" , efforts : [ "none" , "low" , "medium" , "high" ] } ,
{ id : "gpt-5.4" , releaseDate : "2026-03-05" , efforts : [ "none" , "low" , "medium" , "high" , "xhigh" ] } ,
2026-05-08 03:33:55 +00:00
{
id : "gpt-5.5" ,
modelID : "gpt-5-5" ,
releaseDate : "2026-04-23" ,
efforts : [ "none" , "low" , "medium" , "high" , "xhigh" ] ,
} ,
2026-05-08 03:32:47 +00:00
{ id : "gpt-5.4-pro" , releaseDate : "2026-03-05" , efforts : [ "medium" , "high" , "xhigh" ] } ,
{ id : "gpt-5.5-pro" , releaseDate : "2026-04-23" , efforts : [ "medium" , "high" , "xhigh" ] } ,
{ id : "gpt-5-codex" , releaseDate : "2025-09-23" , efforts : [ "low" , "medium" , "high" ] } ,
{ id : "gpt-5.1-codex" , releaseDate : "2025-11-13" , efforts : [ "low" , "medium" , "high" ] } ,
{ id : "gpt-5.1-codex-max" , releaseDate : "2025-11-13" , efforts : [ "low" , "medium" , "high" , "xhigh" ] } ,
{ id : "gpt-5.2-codex" , releaseDate : "2025-12-11" , efforts : [ "low" , "medium" , "high" , "xhigh" ] } ,
{ id : "gpt-5.3-codex" , releaseDate : "2026-01-22" , efforts : [ "none" , "low" , "medium" , "high" , "xhigh" ] } ,
{ id : "gpt-5.3-codex-max" , releaseDate : "2026-01-22" , efforts : [ "none" , "low" , "medium" , "high" , "xhigh" ] } ,
{ id : "gpt-5-chat-latest" , releaseDate : "2025-08-07" , efforts : [ ] } ,
{ id : "gpt-5.1-chat-latest" , releaseDate : "2025-11-13" , efforts : [ "medium" ] } ,
{ id : "gpt-5.2-chat-latest" , releaseDate : "2025-12-11" , efforts : [ "medium" ] } ,
] ) {
test ( ` ${ testCase . id } returns supported reasoning efforts ` , ( ) = > {
const result = ProviderTransform . variants (
createMockModel ( {
id : testCase.modelID ? ? testCase . id ,
providerID : "openai" ,
api : {
id : testCase.id ,
url : "https://api.openai.com" ,
npm : "@ai-sdk/openai" ,
} ,
release_date : testCase.releaseDate ,
} ) ,
)
expect ( Object . keys ( result ) ) . toEqual ( testCase . efforts )
2026-05-05 21:55:08 +00:00
} )
2026-05-08 03:32:47 +00:00
}
2026-05-05 21:55:08 +00:00
test ( "gpt-50 (lookalike) does not get gpt-5 family treatment" , ( ) = > {
const model = createMockModel ( {
id : "gpt-50" ,
providerID : "openai" ,
api : {
id : "gpt-50" ,
url : "https://api.openai.com" ,
npm : "@ai-sdk/openai" ,
} ,
release_date : "2024-01-01" ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" ] )
} )
2025-12-30 22:37:32 +00:00
} )
2026-06-04 05:09:12 +00:00
describe ( "@ai-sdk/amazon-bedrock/mantle" , ( ) = > {
test ( "gpt-5.5 returns OpenAI-style reasoning variants" , ( ) = > {
const model = createMockModel ( {
id : "openai.gpt-5.5" ,
providerID : "amazon-bedrock" ,
api : {
id : "openai.gpt-5.5" ,
url : "https://bedrock-mantle.us-east-2.api.aws/openai/v1" ,
npm : "@ai-sdk/amazon-bedrock/mantle" ,
} ,
release_date : "2026-04-23" ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "none" , "low" , "medium" , "high" , "xhigh" ] )
expect ( result . medium ) . toEqual ( {
reasoningEffort : "medium" ,
reasoningSummary : "auto" ,
include : [ "reasoning.encrypted_content" ] ,
} )
} )
} )
2025-12-30 22:37:32 +00:00
describe ( "@ai-sdk/anthropic" , ( ) = > {
2026-05-08 04:06:07 +00:00
for ( const testCase of [
{
name : "opus 4.5" ,
apiIds : [ "claude-opus-4-5-20251101" , "claude-opus-4.5-20251101" ] ,
efforts : [ "low" , "medium" , "high" ] ,
expectedHigh : { effort : "high" } ,
} ,
{
name : "sonnet 4.6" ,
apiIds : [ "claude-sonnet-4-6" , "claude-sonnet-4.6" ] ,
efforts : [ "low" , "medium" , "high" , "max" ] ,
expectedHigh : { thinking : { type : "adaptive" } , effort : "high" } ,
} ,
{
name : "opus 4.6" ,
apiIds : [ "claude-opus-4-6" , "claude-opus-4.6" ] ,
efforts : [ "low" , "medium" , "high" , "max" ] ,
expectedHigh : { thinking : { type : "adaptive" } , effort : "high" } ,
} ,
{
name : "opus 4.7" ,
apiIds : [ "claude-opus-4-7" , "claude-opus-4.7" ] ,
efforts : [ "low" , "medium" , "high" , "xhigh" , "max" ] ,
expectedHigh : { thinking : { type : "adaptive" , display : "summarized" } , effort : "high" } ,
} ,
2026-05-28 17:19:34 +00:00
{
name : "opus 4.8" ,
apiIds : [ "claude-opus-4-8" , "claude-opus-4.8" ] ,
efforts : [ "low" , "medium" , "high" , "xhigh" , "max" ] ,
expectedHigh : { thinking : { type : "adaptive" , display : "summarized" } , effort : "high" } ,
} ,
2026-06-09 17:38:53 +00:00
{
name : "fable 5" ,
apiIds : [ "claude-fable-5" ] ,
efforts : [ "low" , "medium" , "high" , "xhigh" , "max" ] ,
expectedHigh : { thinking : { type : "adaptive" , display : "summarized" } , effort : "high" } ,
} ,
2026-05-08 04:06:07 +00:00
] ) {
for ( const apiId of testCase . apiIds ) {
test ( ` ${ testCase . name } ${ apiId } returns supported reasoning efforts ` , ( ) = > {
const result = ProviderTransform . variants (
createMockModel ( {
id : ` anthropic/ ${ apiId } ` ,
providerID : "anthropic" ,
api : {
id : apiId ,
url : "https://api.anthropic.com" ,
npm : "@ai-sdk/anthropic" ,
} ,
} ) ,
)
expect ( Object . keys ( result ) ) . toEqual ( testCase . efforts )
expect ( result . high ) . toEqual ( testCase . expectedHigh )
} )
}
}
2026-04-16 15:57:36 +00:00
2026-04-28 19:58:51 +00:00
test ( "github copilot opus 4.7 returns only medium reasoning effort" , ( ) = > {
const model = createMockModel ( {
id : "claude-opus-4.7" ,
providerID : "github-copilot" ,
api : {
id : "claude-opus-4.7" ,
url : "https://api.githubcopilot.com/v1" ,
npm : "@ai-sdk/anthropic" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( result ) . toEqual ( {
medium : {
thinking : {
type : "adaptive" ,
display : "summarized" ,
} ,
effort : "medium" ,
} ,
} )
} )
2025-12-30 22:37:32 +00:00
test ( "returns high and max with thinking config" , ( ) = > {
const model = createMockModel ( {
id : "anthropic/claude-4" ,
providerID : "anthropic" ,
api : {
id : "claude-4" ,
url : "https://api.anthropic.com" ,
npm : "@ai-sdk/anthropic" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "high" , "max" ] )
expect ( result . high ) . toEqual ( {
thinking : {
type : "enabled" ,
budgetTokens : 16000 ,
} ,
} )
expect ( result . max ) . toEqual ( {
thinking : {
type : "enabled" ,
budgetTokens : 31999 ,
} ,
} )
} )
} )
2026-05-29 16:35:26 +00:00
describe ( "@ai-sdk/google-vertex/anthropic" , ( ) = > {
test ( "opus 4.8 uses adaptive reasoning for Vertex model IDs" , ( ) = > {
const result = ProviderTransform . variants (
createMockModel ( {
id : "google-vertex-anthropic/claude-opus-4-8@default" ,
providerID : "google-vertex-anthropic" ,
api : {
id : "claude-opus-4-8@default" ,
url : "https://us-central1-aiplatform.googleapis.com" ,
npm : "@ai-sdk/google-vertex/anthropic" ,
} ,
} ) ,
)
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" , "xhigh" , "max" ] )
expect ( result . high ) . toEqual ( {
thinking : {
type : "adaptive" ,
display : "summarized" ,
} ,
effort : "high" ,
} )
} )
} )
2025-12-30 22:37:32 +00:00
describe ( "@ai-sdk/amazon-bedrock" , ( ) = > {
2026-02-19 22:17:57 +00:00
test ( "anthropic sonnet 4.6 returns adaptive reasoning options" , ( ) = > {
const model = createMockModel ( {
id : "bedrock/anthropic-claude-sonnet-4-6" ,
providerID : "bedrock" ,
api : {
id : "anthropic.claude-sonnet-4-6" ,
url : "https://bedrock.amazonaws.com" ,
npm : "@ai-sdk/amazon-bedrock" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" , "max" ] )
expect ( result . max ) . toEqual ( {
reasoningConfig : {
2026-04-16 15:57:36 +00:00
type : "adaptive" ,
maxReasoningEffort : "max" ,
} ,
} )
} )
test ( "anthropic opus 4.7 returns adaptive reasoning options with xhigh" , ( ) = > {
const model = createMockModel ( {
id : "bedrock/anthropic-claude-opus-4-7" ,
providerID : "bedrock" ,
api : {
id : "anthropic.claude-opus-4-7" ,
url : "https://bedrock.amazonaws.com" ,
npm : "@ai-sdk/amazon-bedrock" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" , "xhigh" , "max" ] )
expect ( result . xhigh ) . toEqual ( {
reasoningConfig : {
type : "adaptive" ,
maxReasoningEffort : "xhigh" ,
2026-04-18 22:46:15 +00:00
display : "summarized" ,
2026-04-16 15:57:36 +00:00
} ,
} )
expect ( result . max ) . toEqual ( {
reasoningConfig : {
2026-02-19 22:17:57 +00:00
type : "adaptive" ,
maxReasoningEffort : "max" ,
2026-04-18 22:46:15 +00:00
display : "summarized" ,
2026-02-19 22:17:57 +00:00
} ,
} )
} )
2026-05-28 17:19:34 +00:00
test ( "anthropic opus 4.8 returns adaptive reasoning options with xhigh" , ( ) = > {
const result = ProviderTransform . variants (
createMockModel ( {
id : "bedrock/anthropic-claude-opus-4.8" ,
providerID : "bedrock" ,
api : {
id : "anthropic.claude-opus-4.8" ,
url : "https://bedrock.amazonaws.com" ,
npm : "@ai-sdk/amazon-bedrock" ,
} ,
} ) ,
)
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" , "xhigh" , "max" ] )
expect ( result . high ) . toEqual ( {
reasoningConfig : {
type : "adaptive" ,
maxReasoningEffort : "high" ,
display : "summarized" ,
} ,
} )
} )
2025-12-30 22:37:32 +00:00
test ( "returns WIDELY_SUPPORTED_EFFORTS with reasoningConfig" , ( ) = > {
const model = createMockModel ( {
id : "bedrock/llama-4" ,
providerID : "bedrock" ,
api : {
id : "llama-4-sc" ,
url : "https://bedrock.amazonaws.com" ,
npm : "@ai-sdk/amazon-bedrock" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" ] )
expect ( result . low ) . toEqual ( {
reasoningConfig : {
type : "enabled" ,
maxReasoningEffort : "low" ,
} ,
} )
} )
} )
2026-05-08 14:22:45 +00:00
for ( const provider of [
{ name : "@ai-sdk/google" , providerID : "google" , url : "https://generativelanguage.googleapis.com" } ,
{ name : "@ai-sdk/google-vertex" , providerID : "google-vertex" , url : "https://vertexai.googleapis.com" } ,
] ) {
describe ( provider . name , ( ) = > {
for ( const testCase of [
{
apiId : "gemini-2.5-pro" ,
efforts : [ "high" , "max" ] ,
expectedHigh : { thinkingConfig : { includeThoughts : true , thinkingBudget : 16_000 } } ,
expectedMax : { thinkingConfig : { includeThoughts : true , thinkingBudget : 32_768 } } ,
} ,
{
apiId : "gemini-2.5-flash" ,
efforts : [ "high" , "max" ] ,
expectedHigh : { thinkingConfig : { includeThoughts : true , thinkingBudget : 16_000 } } ,
expectedMax : { thinkingConfig : { includeThoughts : true , thinkingBudget : 24_576 } } ,
} ,
{
apiId : "gemini-3-pro-preview" ,
efforts : [ "low" , "medium" , "high" ] ,
expectedHigh : { thinkingConfig : { includeThoughts : true , thinkingLevel : "high" } } ,
} ,
{
apiId : "gemini-3.1-pro-preview" ,
efforts : [ "low" , "medium" , "high" ] ,
expectedHigh : { thinkingConfig : { includeThoughts : true , thinkingLevel : "high" } } ,
} ,
{
apiId : "gemini-3-flash-preview" ,
efforts : [ "minimal" , "low" , "medium" , "high" ] ,
expectedHigh : { thinkingConfig : { includeThoughts : true , thinkingLevel : "high" } } ,
} ,
{
apiId : "gemini-3.1-flash-lite" ,
efforts : [ "minimal" , "low" , "medium" , "high" ] ,
expectedHigh : { thinkingConfig : { includeThoughts : true , thinkingLevel : "high" } } ,
} ,
{
apiId : "gemini-3.1-flash-image-preview" ,
efforts : [ "minimal" , "high" ] ,
expectedHigh : { thinkingConfig : { includeThoughts : true , thinkingLevel : "high" } } ,
} ,
{
apiId : "gemini-3-pro-image-preview" ,
efforts : [ "high" ] ,
expectedHigh : { thinkingConfig : { includeThoughts : true , thinkingLevel : "high" } } ,
} ,
] ) {
test ( ` ${ testCase . apiId } returns supported thinking controls ` , ( ) = > {
const result = ProviderTransform . variants (
createMockModel ( {
id : ` ${ provider . providerID } / ${ testCase . apiId } ` ,
providerID : provider.providerID ,
api : {
id : testCase.apiId ,
url : provider.url ,
npm : provider.name ,
} ,
} ) ,
)
expect ( Object . keys ( result ) ) . toEqual ( testCase . efforts )
expect ( result . high ) . toEqual ( testCase . expectedHigh )
if ( testCase . expectedMax ) expect ( result . max ) . toEqual ( testCase . expectedMax )
} )
}
2025-12-30 22:37:32 +00:00
} )
2026-05-08 14:22:45 +00:00
}
2025-12-30 22:37:32 +00:00
describe ( "@ai-sdk/cohere" , ( ) = > {
test ( "returns empty object" , ( ) = > {
const model = createMockModel ( {
id : "cohere/command-r" ,
providerID : "cohere" ,
api : {
id : "command-r" ,
url : "https://api.cohere.com" ,
npm : "@ai-sdk/cohere" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( result ) . toEqual ( { } )
} )
} )
describe ( "@ai-sdk/groq" , ( ) = > {
test ( "returns none and WIDELY_SUPPORTED_EFFORTS with thinkingLevel" , ( ) = > {
const model = createMockModel ( {
id : "groq/llama-4" ,
providerID : "groq" ,
api : {
id : "llama-4-sc" ,
url : "https://api.groq.com" ,
npm : "@ai-sdk/groq" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( Object . keys ( result ) ) . toEqual ( [ "none" , "low" , "medium" , "high" ] )
expect ( result . none ) . toEqual ( {
2026-02-19 18:35:51 +00:00
reasoningEffort : "none" ,
2025-12-30 22:37:32 +00:00
} )
expect ( result . low ) . toEqual ( {
2026-02-19 18:35:51 +00:00
reasoningEffort : "low" ,
2025-12-30 22:37:32 +00:00
} )
} )
} )
describe ( "@ai-sdk/perplexity" , ( ) = > {
test ( "returns empty object" , ( ) = > {
const model = createMockModel ( {
id : "perplexity/sonar-plus" ,
providerID : "perplexity" ,
api : {
id : "sonar-plus" ,
url : "https://api.perplexity.ai" ,
npm : "@ai-sdk/perplexity" ,
} ,
} )
const result = ProviderTransform . variants ( model )
expect ( result ) . toEqual ( { } )
} )
} )
2026-03-10 15:05:45 +00:00
describe ( "@jerome-benoit/sap-ai-provider-v2" , ( ) = > {
2026-06-03 23:40:39 +00:00
const sapModel = ( apiId : string , releaseDate = "2024-01-01" ) = >
2026-05-30 23:30:21 +00:00
createMockModel ( {
id : ` sap-ai-core/ ${ apiId } ` ,
2026-03-10 15:05:45 +00:00
providerID : "sap-ai-core" ,
api : {
2026-05-30 23:30:21 +00:00
id : apiId ,
2026-03-10 15:05:45 +00:00
url : "https://api.ai.sap" ,
npm : "@jerome-benoit/sap-ai-provider-v2" ,
} ,
2026-06-03 23:40:39 +00:00
release_date : releaseDate ,
2026-03-10 15:05:45 +00:00
} )
2026-05-30 23:30:21 +00:00
for ( const testCase of [
{
name : "sonnet 4.6" ,
apiIds : [ "anthropic--claude-sonnet-4-6" ] ,
efforts : [ "low" , "medium" , "high" , "max" ] ,
2026-06-03 23:40:39 +00:00
thinking : { type : "adaptive" } ,
2026-05-30 23:30:21 +00:00
} ,
{
name : "opus 4.6" ,
apiIds : [ "anthropic--claude-4.6-opus" , "anthropic--claude-4-6-opus" ] ,
efforts : [ "low" , "medium" , "high" , "max" ] ,
2026-06-03 23:40:39 +00:00
thinking : { type : "adaptive" } ,
2026-05-30 23:30:21 +00:00
} ,
{
name : "opus 4.7" ,
apiIds : [ "anthropic--claude-4.7-opus" , "anthropic--claude-4-7-opus" ] ,
efforts : [ "low" , "medium" , "high" , "xhigh" , "max" ] ,
2026-06-03 23:40:39 +00:00
thinking : { type : "adaptive" , display : "summarized" } ,
2026-05-30 23:30:21 +00:00
} ,
{
name : "opus 4.8" ,
apiIds : [ "anthropic--claude-4.8-opus" , "anthropic--claude-4-8-opus" ] ,
efforts : [ "low" , "medium" , "high" , "xhigh" , "max" ] ,
2026-06-03 23:40:39 +00:00
thinking : { type : "adaptive" , display : "summarized" } ,
2026-05-30 23:30:21 +00:00
} ,
] ) {
for ( const apiId of testCase . apiIds ) {
2026-06-03 23:40:39 +00:00
test ( ` ${ testCase . name } ${ apiId } returns adaptive thinking variants under modelParams ` , ( ) = > {
2026-05-30 23:30:21 +00:00
const result = ProviderTransform . variants ( sapModel ( apiId ) )
expect ( Object . keys ( result ) ) . toEqual ( testCase . efforts )
2026-06-03 23:40:39 +00:00
for ( const effort of testCase . efforts ) {
expect ( result [ effort ] ) . toEqual ( {
modelParams : {
thinking : testCase.thinking ,
output_config : { effort } ,
} ,
} )
2026-05-30 23:30:21 +00:00
}
} )
}
}
2026-03-10 15:05:45 +00:00
2026-06-03 23:40:39 +00:00
for ( const apiId of [ "anthropic--claude-sonnet-4" , "anthropic--claude-4.5-opus" ] ) {
test ( ` ${ apiId } returns budget_tokens variants under modelParams ` , ( ) = > {
2026-05-30 23:30:21 +00:00
const result = ProviderTransform . variants ( sapModel ( apiId ) )
2026-06-03 23:40:39 +00:00
expect ( Object . keys ( result ) ) . toEqual ( [ "high" , "max" ] )
expect ( result . high ) . toEqual ( {
modelParams : { thinking : { type : "enabled" , budget_tokens : 16000 } } ,
} )
expect ( result . max ) . toEqual ( {
modelParams : { thinking : { type : "enabled" , budget_tokens : 31999 } } ,
} )
2026-03-10 15:05:45 +00:00
} )
2026-05-30 23:30:21 +00:00
}
2026-03-10 15:05:45 +00:00
2026-06-03 23:40:39 +00:00
for ( const testCase of [
{ apiId : "gemini-2.5-pro" , maxBudget : 32768 } ,
{ apiId : "gemini-2.5-flash" , maxBudget : 24576 } ,
] ) {
test ( ` ${ testCase . apiId } returns thinkingConfig variants under modelParams ` , ( ) = > {
const result = ProviderTransform . variants ( sapModel ( testCase . apiId ) )
expect ( Object . keys ( result ) ) . toEqual ( [ "high" , "max" ] )
expect ( result . high ) . toEqual ( {
modelParams : { thinkingConfig : { includeThoughts : true , thinkingBudget : 16000 } } ,
} )
expect ( result . max ) . toEqual ( {
modelParams : { thinkingConfig : { includeThoughts : true , thinkingBudget : testCase.maxBudget } } ,
} )
2026-03-10 15:05:45 +00:00
} )
2026-05-30 23:30:21 +00:00
}
2026-03-10 15:05:45 +00:00
2026-06-03 23:40:39 +00:00
for ( const testCase of [
{ apiId : "gpt-5" , releaseDate : "2025-08-07" , efforts : [ "minimal" , "low" , "medium" , "high" ] } ,
{ apiId : "gpt-5-mini" , releaseDate : "2025-08-07" , efforts : [ "minimal" , "low" , "medium" , "high" ] } ,
{ apiId : "gpt-5-nano" , releaseDate : "2025-08-07" , efforts : [ "minimal" , "low" , "medium" , "high" ] } ,
{ apiId : "gpt-5.4" , releaseDate : "2026-01-15" , efforts : [ "none" , "low" , "medium" , "high" , "xhigh" ] } ,
{ apiId : "azure-openai--o3-mini" , releaseDate : "2024-01-01" , efforts : [ "low" , "medium" , "high" ] } ,
] ) {
test ( ` ${ testCase . apiId } returns reasoning_effort variants under modelParams ` , ( ) = > {
const result = ProviderTransform . variants ( sapModel ( testCase . apiId , testCase . releaseDate ) )
expect ( Object . keys ( result ) ) . toEqual ( testCase . efforts )
for ( const effort of testCase . efforts ) {
expect ( result [ effort ] ) . toEqual ( { modelParams : { reasoning_effort : effort } } )
}
} )
}
for ( const apiId of [
"gemini-3.1-flash-lite" ,
"cohere--command-a-reasoning" ,
"sonar-deep-research" ,
"aws--llama-opus-4.7-fake" ,
] ) {
test ( ` ${ apiId } falls through to harmonized reasoning_effort fallback ` , ( ) = > {
const result = ProviderTransform . variants ( sapModel ( apiId ) )
expect ( Object . keys ( result ) ) . toEqual ( [ "low" , "medium" , "high" ] )
for ( const effort of [ "low" , "medium" , "high" ] ) {
expect ( result [ effort ] ) . toEqual ( { modelParams : { reasoning_effort : effort } } )
}
} )
}
2026-03-10 15:05:45 +00:00
} )
2026-05-05 21:55:08 +00:00
describe ( "ai-gateway-provider (cloudflare-ai-gateway)" , ( ) = > {
const cfModel = ( apiId : string , releaseDate = "2024-01-01" ) = >
createMockModel ( {
id : ` cloudflare-ai-gateway/ ${ apiId } ` ,
providerID : "cloudflare-ai-gateway" ,
api : {
id : apiId ,
url : "https://gateway.ai.cloudflare.com/v1/compat" ,
npm : "ai-gateway-provider" ,
} ,
release_date : releaseDate ,
} )
2026-05-08 03:32:47 +00:00
for ( const testCase of [
{ id : "openai/gpt-5.4" , efforts : [ "none" , "low" , "medium" , "high" , "xhigh" ] } ,
{ id : "openai/gpt-5.2-codex" , efforts : [ "low" , "medium" , "high" , "xhigh" ] } ,
{ id : "openai/gpt-5.3-codex" , efforts : [ "none" , "low" , "medium" , "high" , "xhigh" ] } ,
{ id : "openai/gpt-5-pro" , efforts : [ "high" ] } ,
{ id : "openai/gpt-5.2-pro" , efforts : [ "medium" , "high" , "xhigh" ] } ,
{ id : "openai/gpt-5-chat-latest" , efforts : [ ] } ,
{ id : "openai/gpt-5.2-chat-latest" , efforts : [ "medium" ] } ,
] ) {
test ( ` ${ testCase . id } returns supported reasoning efforts ` , ( ) = > {
const result = ProviderTransform . variants ( cfModel ( testCase . id , "2026-03-05" ) )
expect ( Object . keys ( result ) ) . toEqual ( testCase . efforts )
} )
}
2026-05-05 21:55:08 +00:00
test ( "openai gpt-4o (no reasoning) returns empty" , ( ) = > {
const model = cfModel ( "openai/gpt-4o" )
model . capabilities . reasoning = false
const result = ProviderTransform . variants ( model )
expect ( result ) . toEqual ( { } )
} )
test ( "non-openai upstream falls back to widely-supported OAI efforts" , ( ) = > {
const result = ProviderTransform . variants ( cfModel ( "anthropic/claude-sonnet-4-6" ) )
expect ( result ) . toEqual ( {
low : { reasoningEffort : "low" } ,
medium : { reasoningEffort : "medium" } ,
high : { reasoningEffort : "high" } ,
} )
} )
} )
} )
2026-05-08 03:32:47 +00:00
describe ( "ProviderTransform.smallOptions - gpt-5 chat/search" , ( ) = > {
2026-05-13 23:30:36 +00:00
const createModel = ( apiId : string ) = > {
const model = {
2026-05-08 03:32:47 +00:00
id : ` openai/ ${ apiId } ` ,
providerID : "openai" ,
api : {
id : apiId ,
url : "https://api.openai.com" ,
npm : "@ai-sdk/openai" ,
} ,
2026-05-13 23:30:36 +00:00
capabilities : { reasoning : true } ,
limit : { output : 64_000 } ,
release_date : "2026-01-01" ,
} as any
model . variants = ProviderTransform . variants ( model )
return model
}
2026-05-08 03:32:47 +00:00
for ( const testCase of [
{ id : "gpt-5-chat-latest" , options : { store : false } } ,
2026-05-13 23:30:36 +00:00
{
id : "gpt-5.1-chat-latest" ,
options : {
store : false ,
reasoningEffort : "medium" ,
reasoningSummary : "auto" ,
include : [ "reasoning.encrypted_content" ] ,
} ,
} ,
{
id : "gpt-5.2-chat-latest" ,
options : {
store : false ,
reasoningEffort : "medium" ,
reasoningSummary : "auto" ,
include : [ "reasoning.encrypted_content" ] ,
} ,
} ,
{
id : "gpt-5-search-api" ,
options : {
store : false ,
reasoningEffort : "none" ,
reasoningSummary : "auto" ,
include : [ "reasoning.encrypted_content" ] ,
} ,
} ,
2026-05-08 03:32:47 +00:00
] ) {
test ( ` ${ testCase . id } returns only supported small options ` , ( ) = > {
expect ( ProviderTransform . smallOptions ( createModel ( testCase . id ) ) ) . toEqual ( testCase . options )
} )
}
} )
2026-06-09 01:22:02 +00:00
test ( "ProviderTransform.smallOptions disables OpenRouter reasoning when the weakest effort is low" , ( ) = > {
expect (
ProviderTransform . smallOptions ( {
providerID : "openrouter" ,
api : {
id : "anthropic/claude-sonnet-4.6" ,
npm : "@openrouter/ai-sdk-provider" ,
} ,
variants : {
low : { reasoning : { effort : "low" } } ,
medium : { reasoning : { effort : "medium" } } ,
high : { reasoning : { effort : "high" } } ,
} ,
} as any ) ,
) . toEqual ( { reasoning : { effort : "none" } } )
} )
2026-05-08 14:22:45 +00:00
describe ( "ProviderTransform.smallOptions - google thinking controls" , ( ) = > {
2026-05-13 23:30:36 +00:00
const createGoogleModel = ( apiId : string ) = > {
const model = {
2026-05-08 14:22:45 +00:00
id : ` google/ ${ apiId } ` ,
providerID : "google" ,
api : {
id : apiId ,
url : "https://generativelanguage.googleapis.com" ,
npm : "@ai-sdk/google" ,
} ,
2026-05-13 23:30:36 +00:00
capabilities : { reasoning : true } ,
limit : { output : 64_000 } ,
} as any
model . variants = ProviderTransform . variants ( model )
return model
}
2026-05-08 14:22:45 +00:00
for ( const testCase of [
2026-05-13 23:30:36 +00:00
{ id : "gemini-3-pro-preview" , options : { thinkingConfig : { includeThoughts : true , thinkingLevel : "low" } } } ,
{ id : "gemini-3-flash-preview" , options : { thinkingConfig : { includeThoughts : true , thinkingLevel : "minimal" } } } ,
{
id : "gemini-3.1-flash-image-preview" ,
options : { thinkingConfig : { includeThoughts : true , thinkingLevel : "minimal" } } ,
} ,
{ id : "gemini-3-pro-image-preview" , options : { thinkingConfig : { includeThoughts : true , thinkingLevel : "high" } } } ,
{ id : "gemini-2.5-pro" , options : { thinkingConfig : { includeThoughts : true , thinkingBudget : 16000 } } } ,
{ id : "gemini-2.5-flash" , options : { thinkingConfig : { includeThoughts : true , thinkingBudget : 16000 } } } ,
2026-05-08 14:22:45 +00:00
] ) {
test ( ` ${ testCase . id } returns supported small thinking options ` , ( ) = > {
expect ( ProviderTransform . smallOptions ( createGoogleModel ( testCase . id ) ) ) . toEqual ( testCase . options )
} )
}
2026-05-13 23:30:36 +00:00
test ( "uses the first configured variant when available" , ( ) = > {
expect (
ProviderTransform . smallOptions ( {
. . . createGoogleModel ( "gemini-2.5-pro" ) ,
variants : {
high : { thinkingConfig : { includeThoughts : true , thinkingBudget : 16000 } } ,
max : { thinkingConfig : { includeThoughts : true , thinkingBudget : 32768 } } ,
} ,
} ) ,
) . toEqual ( { thinkingConfig : { includeThoughts : true , thinkingBudget : 16000 } } )
} )
test ( "does not synthesize thinking options when variants are empty" , ( ) = > {
expect ( ProviderTransform . smallOptions ( { . . . createGoogleModel ( "gemini-2.5-pro" ) , variants : { } } ) ) . toEqual ( { } )
} )
2026-05-08 14:22:45 +00:00
} )
2026-05-05 21:55:08 +00:00
describe ( "ProviderTransform.providerOptions - ai-gateway-provider" , ( ) = > {
const createModel = ( overrides : Partial < any > = { } ) = >
( {
id : "cloudflare-ai-gateway/openai/gpt-5.4" ,
providerID : "cloudflare-ai-gateway" ,
api : {
id : "openai/gpt-5.4" ,
url : "https://gateway.ai.cloudflare.com/v1/compat" ,
npm : "ai-gateway-provider" ,
} ,
capabilities : {
temperature : false ,
reasoning : true ,
attachment : true ,
toolcall : true ,
input : { text : true , audio : false , image : true , video : false , pdf : true } ,
output : { text : true , audio : false , image : false , video : false , pdf : false } ,
interleaved : false ,
} ,
cost : { input : 1 , output : 1 , cache : { read : 0 , write : 0 } } ,
limit : { context : 1_000_000 , output : 128_000 } ,
status : "active" ,
options : { } ,
headers : { } ,
release_date : "2026-03-05" ,
. . . overrides ,
} ) as any
test ( "routes options under openaiCompatible (the key @ai-sdk/openai-compatible reads)" , ( ) = > {
// Regression: previously fell back to providerID="cloudflare-ai-gateway",
// which @ai-sdk/openai-compatible never reads, silently dropping reasoningEffort.
const result = ProviderTransform . providerOptions ( createModel ( ) , { reasoningEffort : "high" } )
expect ( result ) . toEqual ( { openaiCompatible : { reasoningEffort : "high" } } )
} )
2025-12-30 22:37:32 +00:00
} )