This repository was archived by the owner on May 15, 2026. It is now read-only.
File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -439,6 +439,51 @@ describe("DeepSeekHandler", () => {
439439 expect ( reasoningChunks [ 1 ] . text ) . toBe ( " I'll analyze step by step." )
440440 } )
441441
442+ it ( "should preserve empty reasoning_content chunks for DeepSeek V4" , async ( ) => {
443+ mockCreate . mockImplementationOnce ( async ( ) => ( {
444+ [ Symbol . asyncIterator ] : async function * ( ) {
445+ yield {
446+ choices : [
447+ {
448+ delta : { reasoning_content : "" } ,
449+ index : 0 ,
450+ } ,
451+ ] ,
452+ usage : null ,
453+ }
454+ yield {
455+ choices : [
456+ {
457+ delta : { } ,
458+ index : 0 ,
459+ finish_reason : "stop" ,
460+ } ,
461+ ] ,
462+ usage : {
463+ prompt_tokens : 10 ,
464+ completion_tokens : 5 ,
465+ total_tokens : 15 ,
466+ } ,
467+ }
468+ } ,
469+ } ) )
470+
471+ const reasonerHandler = new DeepSeekHandler ( {
472+ ...mockOptions ,
473+ apiModelId : "deepseek-v4-pro" ,
474+ } )
475+
476+ const chunks : any [ ] = [ ]
477+ for await ( const chunk of reasonerHandler . createMessage ( systemPrompt , messages ) ) {
478+ chunks . push ( chunk )
479+ }
480+
481+ expect ( chunks . filter ( ( chunk ) => chunk . type === "reasoning" ) ) . toContainEqual ( {
482+ type : "reasoning" ,
483+ text : "" ,
484+ } )
485+ } )
486+
442487 it ( "should pass thinking parameter for DeepSeek V4 models" , async ( ) => {
443488 const reasonerHandler = new DeepSeekHandler ( {
444489 ...mockOptions ,
Original file line number Diff line number Diff line change @@ -463,6 +463,57 @@ describe("OpenAiHandler", () => {
463463 expect ( callArgs . reasoning_effort ) . toBeUndefined ( )
464464 } )
465465
466+ it ( "should preserve empty DeepSeek V4 reasoning_content chunks for OpenAI-compatible endpoints" , async ( ) => {
467+ mockCreate . mockImplementationOnce ( async ( ) => ( {
468+ [ Symbol . asyncIterator ] : async function * ( ) {
469+ yield {
470+ choices : [
471+ {
472+ delta : { reasoning_content : "" } ,
473+ index : 0 ,
474+ } ,
475+ ] ,
476+ usage : null ,
477+ }
478+ yield {
479+ choices : [
480+ {
481+ delta : { } ,
482+ index : 0 ,
483+ finish_reason : "stop" ,
484+ } ,
485+ ] ,
486+ usage : {
487+ prompt_tokens : 10 ,
488+ completion_tokens : 5 ,
489+ total_tokens : 15 ,
490+ } ,
491+ }
492+ } ,
493+ } ) )
494+
495+ const deepSeekV4Handler = new OpenAiHandler ( {
496+ ...mockOptions ,
497+ openAiBaseUrl : "https://api.deepseek.com" ,
498+ openAiModelId : "deepseek-v4-flash" ,
499+ openAiCustomModelInfo : {
500+ contextWindow : 1_000_000 ,
501+ supportsPromptCache : true ,
502+ preserveReasoning : true ,
503+ } ,
504+ } )
505+
506+ const chunks : any [ ] = [ ]
507+ for await ( const chunk of deepSeekV4Handler . createMessage ( systemPrompt , messages ) ) {
508+ chunks . push ( chunk )
509+ }
510+
511+ expect ( chunks . filter ( ( chunk ) => chunk . type === "reasoning" ) ) . toContainEqual ( {
512+ type : "reasoning" ,
513+ text : "" ,
514+ } )
515+ } )
516+
466517 it ( "should include max_tokens when includeMaxTokens is true" , async ( ) => {
467518 const optionsWithMaxTokens : ApiHandlerOptions = {
468519 ...mockOptions ,
Original file line number Diff line number Diff line change @@ -125,10 +125,10 @@ export class DeepSeekHandler extends OpenAiHandler {
125125
126126 // Handle reasoning_content from DeepSeek's interleaved thinking
127127 // This is the proper way DeepSeek sends thinking content in streaming
128- if ( "reasoning_content" in delta && delta . reasoning_content ) {
128+ if ( "reasoning_content" in delta && typeof delta . reasoning_content === "string" ) {
129129 yield {
130130 type : "reasoning" ,
131- text : ( delta . reasoning_content as string ) || "" ,
131+ text : delta . reasoning_content ,
132132 }
133133 }
134134
Original file line number Diff line number Diff line change @@ -231,10 +231,10 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
231231 }
232232 }
233233
234- if ( "reasoning_content" in delta && delta . reasoning_content ) {
234+ if ( "reasoning_content" in delta && typeof delta . reasoning_content === "string" ) {
235235 yield {
236236 type : "reasoning" ,
237- text : ( delta . reasoning_content as string | undefined ) || "" ,
237+ text : delta . reasoning_content ,
238238 }
239239 }
240240
You can’t perform that action at this time.
0 commit comments