Skip to content

Commit 0ccb0a0

Browse files
committed
fix: plumb thinking blocks between litellm and gen ai sdk parts
1 parent d45b31f commit 0ccb0a0

File tree

1 file changed

+55
-12
lines changed

1 file changed

+55
-12
lines changed

src/google/adk/models/lite_llm.py

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)