@@ -566,6 +566,26 @@ export function topK(model: Provider.Model) {
566566}
567567
568568const WIDELY_SUPPORTED_EFFORTS = [ "low" , "medium" , "high" ]
569+ // DeepSeek V4 effort tiers on the official API. `low`/`xhigh` are compatibility
570+ // values the server maps (low→high on v4-pro, xhigh→max), so only tiers the
571+ // selected model actually honors are exposed. Flash supports all three; pro
572+ // honors high/max until it gains the full set in early Aug 2026.
573+ // See https://api-docs.deepseek.com/guides/thinking_mode
574+ function deepseekV4Efforts ( apiId : string ) : string [ ] {
575+ return apiId . toLowerCase ( ) . includes ( "deepseek-v4-flash" ) ? [ "low" , "high" , "max" ] : [ "high" , "max" ]
576+ }
577+
578+ // DeepSeek's own OpenAI-compatible endpoint (api.deepseek.com) is the only
579+ // transport that honors the native `thinking` object and flash's low tier;
580+ // mirrors keep their own toggle surface (e.g. DashScope's enable_thinking).
581+ function isDeepseekV4Official ( model : { providerID : string ; api : { npm : string ; id : string } } ) {
582+ return (
583+ model . providerID === "deepseek" &&
584+ model . api . npm === "@ai-sdk/openai-compatible" &&
585+ model . api . id . toLowerCase ( ) . includes ( "deepseek-v4" )
586+ )
587+ }
588+
569589const OPENAI_EFFORTS = [ "none" , "minimal" , ...WIDELY_SUPPORTED_EFFORTS , "xhigh" ]
570590const OPENAI_GPT5_1_EFFORTS = [ "none" , ...WIDELY_SUPPORTED_EFFORTS ]
571591const OPENAI_GPT5_2_PLUS_EFFORTS = [ ...OPENAI_GPT5_1_EFFORTS , "xhigh" ]
@@ -926,10 +946,14 @@ export function variants(model: Provider.Model): Record<string, Record<string, a
926946 if ( model . api . id . toLowerCase ( ) . includes ( "north-mini-code" ) ) {
927947 return Object . fromEntries ( [ "none" , "high" ] . map ( ( effort ) => [ effort , { reasoningEffort : effort } ] ) )
928948 }
929- const efforts = [ ...WIDELY_SUPPORTED_EFFORTS ]
930949 if ( model . api . id . toLowerCase ( ) . includes ( "deepseek-v4" ) ) {
931- efforts . push ( "max" )
950+ // Official API exposes the native thinking toggle and (flash) the low
951+ // tier; mirrors share the model's high/max effort surface but not the
952+ // official-only toggle and low tier.
953+ const efforts = isDeepseekV4Official ( model ) ? deepseekV4Efforts ( model . api . id ) : [ "high" , "max" ]
954+ return toggleAndEffort ( model , effortVariants ( model , efforts ) , isDeepseekV4Official ( model ) )
932955 }
956+ const efforts = [ ...WIDELY_SUPPORTED_EFFORTS ]
933957 return Object . fromEntries ( efforts . map ( ( effort ) => [ effort , { reasoningEffort : effort } ] ) )
934958
935959 case "@ai-sdk/azure" :
@@ -1203,6 +1227,15 @@ export function options(input: {
12031227 }
12041228 }
12051229
1230+ // DeepSeek V4 thinking mode defaults to enabled, but send the native toggle
1231+ // explicitly: the docs require an explicit `thinking.type` when combining
1232+ // with `response_format`, and explicit beats implicit if defaults ever change.
1233+ // Variants may override this (none → thinking.type disabled), since they are
1234+ // merged over these options at request time.
1235+ if ( isDeepseekV4Official ( input . model ) ) {
1236+ result [ "thinking" ] = { type : "enabled" }
1237+ }
1238+
12061239 if ( input . model . providerID === "meta" && input . model . api . npm === "@ai-sdk/openai" ) {
12071240 result [ "reasoningSummary" ] = "auto"
12081241 result [ "include" ] = INCLUDE_ENCRYPTED_REASONING
@@ -1645,9 +1678,23 @@ export function reasoningVariants(model: ModelsDev.Model, target: Provider.Model
16451678 if ( options . length === 0 ) return { }
16461679
16471680 const effort = options . find ( ( option ) => option . type === "effort" )
1648- if ( effort ) return effortVariants ( target , effort . values )
1649-
16501681 const toggle = options . some ( ( option ) => option . type === "toggle" )
1682+ if ( effort ) {
1683+ let values = effort . values
1684+ // models.dev lists [high, max] for deepseek-v4-flash, but the official API
1685+ // also honors `low` — expose the full tier set so the fast lane can go faster.
1686+ if ( isDeepseekV4Official ( target ) ) {
1687+ values = unique ( [ ...deepseekV4Efforts ( target . api . id ) , ...values ] )
1688+ }
1689+ const variants = effortVariants ( target , values )
1690+ // Unsupported effort controls yield no variants (metadata-declared effort
1691+ // is not replaced by heuristic fallback); a declared toggle still applies.
1692+ if ( Object . keys ( variants ) . length === 0 ) return toggle ? nonEmptyVariants ( reasoningToggle ( target ) ) : { }
1693+ // mergeDeep (not spread) so toggle layers keep their own keys where effort
1694+ // variants share one — e.g. deepseek-v4 "high" carries thinking.enabled.
1695+ return toggleAndEffort ( target , variants , toggle )
1696+ }
1697+
16511698 const budget = options . find ( ( option ) => option . type === "budget_tokens" )
16521699 if ( ! budget ) return toggle ? nonEmptyVariants ( reasoningToggle ( target ) ) : undefined
16531700
@@ -1690,17 +1737,33 @@ function nonEmptyVariants(variants: NonNullable<Provider.Model["variants"]>): Pr
16901737 return Object . keys ( variants ) . length > 0 ? variants : undefined
16911738}
16921739
1740+ // Combine the native thinking toggle with effort variants when both apply. mergeDeep (not spread)
1741+ // keeps the toggle's own keys where an effort variant shares one — e.g. deepseek-v4 "high"
1742+ // carries thinking.enabled.
1743+ function toggleAndEffort (
1744+ model : Provider . Model ,
1745+ variants : NonNullable < Provider . Model [ "variants" ] > ,
1746+ toggle : boolean ,
1747+ ) : NonNullable < Provider . Model [ "variants" ] > {
1748+ return toggle ? mergeDeep ( reasoningToggle ( model ) , variants ) : variants
1749+ }
1750+
16931751function reasoningToggle ( model : Provider . Model ) : NonNullable < Provider . Model [ "variants" ] > {
16941752 if ( model . api . npm === "@ai-sdk/alibaba" )
16951753 return {
16961754 none : { enableThinking : false } ,
16971755 high : { enableThinking : true } ,
16981756 }
1699- if ( model . api . npm === "@ai-sdk/cohere" )
1757+ if ( model . api . npm === "@ai-sdk/cohere" || isDeepseekV4Official ( model ) ) {
1758+ // DeepSeek V4 toggles thinking via the native `thinking` object on the
1759+ // official API; non-thinking mode is the fast lane for simple tasks.
1760+ // Mirrors serving deepseek-v4 keep their own toggle surface (e.g.
1761+ // DashScope's enable_thinking), so stay official-only.
17001762 return {
17011763 none : { thinking : { type : "disabled" } } ,
17021764 high : { thinking : { type : "enabled" } } ,
17031765 }
1766+ }
17041767 return { }
17051768}
17061769
0 commit comments