@@ -95,6 +95,75 @@ export async function createChatModelFromEndpoint(
9595 streaming,
9696 } ) ;
9797 }
98+
99+ case "deepseek" : {
100+ const { ChatDeepSeek } = await import ( "@langchain/deepseek" ) ;
101+
102+ // Create a subclass that fixes the missing reasoning_content issue.
103+ // Bug: @langchain /deepseek stores reasoning_content in additional_kwargs
104+ // when receiving, but doesn't inject it back when sending requests.
105+ // DeepSeek API requires reasoning_content on every assistant message
106+ // during tool-calling loops, or it returns a 400 error.
107+ class ChatDeepSeekFixed extends ChatDeepSeek {
108+ private _reasoningMap = new Map < number , string > ( ) ;
109+
110+ // biome-ignore lint: override needs any
111+ async _generate ( messages : any [ ] , options : any , runManager ?: any ) {
112+ this . _buildReasoningMap ( messages ) ;
113+ return super . _generate ( messages , options , runManager ) ;
114+ }
115+
116+ // biome-ignore lint: override needs any
117+ async * _streamResponseChunks ( messages : any [ ] , options : any , runManager ?: any ) {
118+ this . _buildReasoningMap ( messages ) ;
119+ yield * super . _streamResponseChunks ( messages , options , runManager ) ;
120+ }
121+
122+ // biome-ignore lint: override needs any
123+ // @ts -expect-error -- overloaded signature; runtime type is correct
124+ async completionWithRetry ( request : any , requestOptions ?: any ) {
125+ // Inject reasoning_content into assistant messages in the API request
126+ if ( request . messages && this . _reasoningMap . size > 0 ) {
127+ let assistantIdx = 0 ;
128+ for ( const msg of request . messages ) {
129+ if ( msg . role === "assistant" ) {
130+ const reasoning = this . _reasoningMap . get ( assistantIdx ) ;
131+ if ( reasoning !== undefined ) {
132+ msg . reasoning_content = reasoning ;
133+ }
134+ assistantIdx ++ ;
135+ }
136+ }
137+ }
138+ return super . completionWithRetry ( request , requestOptions ) ;
139+ }
140+
141+ // biome-ignore lint: messages is BaseMessage[]
142+ private _buildReasoningMap ( messages : any [ ] ) {
143+ this . _reasoningMap . clear ( ) ;
144+ let assistantIdx = 0 ;
145+ for ( const msg of messages ) {
146+ if ( msg . _getType ?.( ) === "ai" || msg . constructor ?. name === "AIMessage" || msg . constructor ?. name === "AIMessageChunk" ) {
147+ const reasoning = msg . additional_kwargs ?. reasoning_content ;
148+ if ( typeof reasoning === "string" ) {
149+ this . _reasoningMap . set ( assistantIdx , reasoning ) ;
150+ }
151+ assistantIdx ++ ;
152+ }
153+ }
154+ }
155+ }
156+
157+ return new ChatDeepSeekFixed ( {
158+ model,
159+ apiKey : endpoint . apiKey ,
160+ configuration : endpoint . baseUrl ? { baseURL : endpoint . baseUrl } : undefined ,
161+ temperature,
162+ maxTokens,
163+ streaming,
164+ } as ConstructorParameters < typeof ChatDeepSeek > [ 0 ] ) ;
165+ }
166+
98167 default : {
99168 const { ChatOpenAI } = await import ( "@langchain/openai" ) ;
100169
0 commit comments