@@ -232,13 +232,15 @@ def _content_to_message_param(
232232
233233 # Handle user or assistant messages
234234 role = _to_litellm_role (content .role )
235- message_content = _get_content (content .parts ) or None
236235
237236 if role == "user" :
237+ message_content = _get_content (content .parts ) or None
238238 return ChatCompletionUserMessage (role = "user" , content = message_content )
239239 else : # assistant/model
240240 tool_calls = []
241- content_present = False
241+ thinking_blocks = []
242+ other_parts = []
243+
242244 for part in content .parts :
243245 if part .function_call :
244246 tool_calls .append (
@@ -251,23 +253,40 @@ def _content_to_message_param(
251253 ),
252254 )
253255 )
254- elif part .text or part .inline_data :
255- content_present = True
256+ elif part .thought :
257+ if (
258+ part .thought_signature
259+ and part .thought_signature .decode ("utf-8" ) == "redacted_thinking"
260+ ):
261+ thinking_block = {
262+ "type" : "redacted_thinking" ,
263+ "data" : part .text ,
264+ }
265+ else :
266+ thinking_block = {"type" : "thinking" }
267+ if part .thought_signature :
268+ thinking_block ["signature" ] = part .thought_signature .decode ("utf-8" )
269+ if part .text :
270+ thinking_block ["thinking" ] = part .text
271+ thinking_blocks .append (thinking_block )
272+ else :
273+ other_parts .append (part )
256274
257- final_content = message_content if content_present else None
258- if final_content and isinstance (final_content , list ):
275+ message_content = _get_content ( other_parts ) or None
276+ if message_content and isinstance (message_content , list ):
259277 # when the content is a single text object, we can use it directly.
260278 # this is needed for ollama_chat provider which fails if content is a list
261- final_content = (
262- final_content [0 ].get ("text" , "" )
263- if final_content [0 ].get ("type" , None ) == "text"
264- else final_content
279+ message_content = (
280+ message_content [0 ].get ("text" , "" )
281+ if message_content [0 ].get ("type" , None ) == "text"
282+ else message_content
265283 )
266284
267285 return ChatCompletionAssistantMessage (
268286 role = role ,
269- content = final_content ,
287+ content = message_content ,
270288 tool_calls = tool_calls or None ,
289+ thinking_blocks = thinking_blocks or None ,
271290 )
272291
273292
@@ -574,6 +593,31 @@ def _message_to_generate_content_response(
574593 if message .get ("content" , None ):
575594 parts .append (types .Part .from_text (text = message .get ("content" )))
576595
596+ if message .get ("thinking_blocks" ):
597+ for block in message .get ("thinking_blocks" ):
598+ if block .get ("type" ) == "thinking" :
599+ signature = block .get ("signature" )
600+ thought = block .get ("thinking" )
601+ part = types .Part (
602+ thought = True ,
603+ thought_signature = signature .encode ("utf-8" ) if signature else None ,
604+ text = thought ,
605+ )
606+ parts .append (part )
607+ elif block .get ("type" ) == "redacted_thinking" :
608+ # Part doesn't have redacted thinking type
609+ # therefore use signature field to show redacted thinking
610+ signature = "redacted_thinking"
611+ thought = block .get ("data" )
612+ part = types .Part (
613+ thought = True ,
614+ thought_signature = signature .encode ("utf-8" ) if signature else None ,
615+ text = thought ,
616+ )
617+ parts .append (part )
618+ else :
619+ logging .warning (f'ignoring unsupported thinking block type { type (block )} ' )
620+
577621 if message .get ("tool_calls" , None ):
578622 for tool_call in message .get ("tool_calls" ):
579623 if tool_call .type == "function" :
@@ -583,7 +627,6 @@ def _message_to_generate_content_response(
583627 )
584628 part .function_call .id = tool_call .id
585629 parts .append (part )
586-
587630 return LlmResponse (
588631 content = types .Content (role = "model" , parts = parts ), partial = is_partial
589632 )
0 commit comments