@@ -139,25 +139,26 @@ function handleAssistantMessage(
139139 ( block ) : block is AnthropicToolUseBlock => block . type === "tool_use" ,
140140 )
141141
142- const textBlocks = message . content . filter (
143- ( block ) : block is AnthropicTextBlock => block . type === "text" ,
144- )
145-
146142 const thinkingBlocks = message . content . filter (
147143 ( block ) : block is AnthropicThinkingBlock => block . type === "thinking" ,
148144 )
149145
150- // Combine text and thinking blocks, as OpenAI doesn't have separate thinking blocks
151- const allTextContent = [
152- ...textBlocks . map ( ( b ) => b . text ) ,
153- ...thinkingBlocks . map ( ( b ) => b . thinking ) ,
154- ] . join ( "\n\n" )
146+ const allThinkingContent = thinkingBlocks
147+ . filter ( ( b ) => b . thinking && b . thinking . length > 0 )
148+ . map ( ( b ) => b . thinking )
149+ . join ( "\n\n" )
150+
151+ const signature = thinkingBlocks . find (
152+ ( b ) => b . signature && b . signature . length > 0 ,
153+ ) ?. signature
155154
156155 return toolUseBlocks . length > 0 ?
157156 [
158157 {
159158 role : "assistant" ,
160- content : allTextContent || null ,
159+ content : mapContent ( message . content ) ,
160+ reasoning_text : allThinkingContent ,
161+ reasoning_opaque : signature ,
161162 tool_calls : toolUseBlocks . map ( ( toolUse ) => ( {
162163 id : toolUse . id ,
163164 type : "function" ,
@@ -172,6 +173,8 @@ function handleAssistantMessage(
172173 {
173174 role : "assistant" ,
174175 content : mapContent ( message . content ) ,
176+ reasoning_text : allThinkingContent ,
177+ reasoning_opaque : signature ,
175178 } ,
176179 ]
177180}
@@ -191,11 +194,8 @@ function mapContent(
191194 const hasImage = content . some ( ( block ) => block . type === "image" )
192195 if ( ! hasImage ) {
193196 return content
194- . filter (
195- ( block ) : block is AnthropicTextBlock | AnthropicThinkingBlock =>
196- block . type === "text" || block . type === "thinking" ,
197- )
198- . map ( ( block ) => ( block . type === "text" ? block . text : block . thinking ) )
197+ . filter ( ( block ) : block is AnthropicTextBlock => block . type === "text" )
198+ . map ( ( block ) => block . text )
199199 . join ( "\n\n" )
200200 }
201201
@@ -204,12 +204,6 @@ function mapContent(
204204 switch ( block . type ) {
205205 case "text" : {
206206 contentParts . push ( { type : "text" , text : block . text } )
207-
208- break
209- }
210- case "thinking" : {
211- contentParts . push ( { type : "text" , text : block . thinking } )
212-
213207 break
214208 }
215209 case "image" : {
@@ -219,7 +213,6 @@ function mapContent(
219213 url : `data:${ block . source . media_type } ;base64,${ block . source . data } ` ,
220214 } ,
221215 } )
222-
223216 break
224217 }
225218 // No default
@@ -282,34 +275,32 @@ export function translateToAnthropic(
282275 response : ChatCompletionResponse ,
283276) : AnthropicResponse {
284277 // Merge content from all choices
285- const allTextBlocks : Array < AnthropicTextBlock > = [ ]
286- const allToolUseBlocks : Array < AnthropicToolUseBlock > = [ ]
287- let stopReason : "stop" | "length" | "tool_calls" | "content_filter" | null =
288- null // default
289- stopReason = response . choices [ 0 ] ?. finish_reason ?? stopReason
278+ const assistantContentBlocks : Array < AnthropicAssistantContentBlock > = [ ]
279+ let stopReason = response . choices [ 0 ] ?. finish_reason ?? null
290280
291281 // Process all choices to extract text and tool use blocks
292282 for ( const choice of response . choices ) {
293283 const textBlocks = getAnthropicTextBlocks ( choice . message . content )
284+ const thingBlocks = getAnthropicThinkBlocks (
285+ choice . message . reasoning_text ,
286+ choice . message . reasoning_opaque ,
287+ )
294288 const toolUseBlocks = getAnthropicToolUseBlocks ( choice . message . tool_calls )
295289
296- allTextBlocks . push ( ...textBlocks )
297- allToolUseBlocks . push ( ...toolUseBlocks )
290+ assistantContentBlocks . push ( ...textBlocks , ...thingBlocks , ...toolUseBlocks )
298291
299292 // Use the finish_reason from the first choice, or prioritize tool_calls
300293 if ( choice . finish_reason === "tool_calls" || stopReason === "stop" ) {
301294 stopReason = choice . finish_reason
302295 }
303296 }
304297
305- // Note: GitHub Copilot doesn't generate thinking blocks, so we don't include them in responses
306-
307298 return {
308299 id : response . id ,
309300 type : "message" ,
310301 role : "assistant" ,
311302 model : response . model ,
312- content : [ ... allTextBlocks , ... allToolUseBlocks ] ,
303+ content : assistantContentBlocks ,
313304 stop_reason : mapOpenAIStopReasonToAnthropic ( stopReason ) ,
314305 stop_sequence : null ,
315306 usage : {
@@ -342,6 +333,31 @@ function getAnthropicTextBlocks(
342333 return [ ]
343334}
344335
336+ function getAnthropicThinkBlocks (
337+ reasoningText : string | null | undefined ,
338+ reasoningOpaque : string | null | undefined ,
339+ ) : Array < AnthropicThinkingBlock > {
340+ if ( reasoningText ) {
341+ return [
342+ {
343+ type : "thinking" ,
344+ thinking : reasoningText ,
345+ signature : "" ,
346+ } ,
347+ ]
348+ }
349+ if ( reasoningOpaque ) {
350+ return [
351+ {
352+ type : "thinking" ,
353+ thinking : "" ,
354+ signature : reasoningOpaque ,
355+ } ,
356+ ]
357+ }
358+ return [ ]
359+ }
360+
345361function getAnthropicToolUseBlocks (
346362 toolCalls : Array < ToolCall > | undefined ,
347363) : Array < AnthropicToolUseBlock > {
0 commit comments