mirror of
https://github.com/tiennm99/litellm.git
synced 2026-06-17 22:48:35 +00:00
144 lines
4.9 KiB
Python
144 lines
4.9 KiB
Python
from litellm.llms.vertex_ai.gemini.vertex_and_google_ai_studio_gemini import (
|
|
VertexGeminiConfig,
|
|
)
|
|
from litellm.llms.vertex_ai.gemini.transformation import (
|
|
_gemini_convert_messages_with_history,
|
|
)
|
|
|
|
|
|
def test_thought_true_creates_thinking_block():
|
|
"""
|
|
Test that a part with thought=True and non-empty text creates a thinking block.
|
|
Per Google's docs, parts must have thought=True to be thinking content.
|
|
"""
|
|
parts = [{"text": "Some thinking", "thought": True, "thoughtSignature": "sig-1"}]
|
|
config = VertexGeminiConfig()
|
|
thinking_blocks = config._extract_thinking_blocks_from_parts(parts)
|
|
assert len(thinking_blocks) == 1
|
|
block = thinking_blocks[0]
|
|
assert block["thinking"] == "Some thinking"
|
|
assert block["signature"] == "sig-1"
|
|
|
|
|
|
def test_thought_true_with_empty_text_creates_block():
|
|
"""
|
|
Test that a part with thought=True but empty text still creates a thinking block.
|
|
"""
|
|
parts = [{"text": "", "thought": True, "thoughtSignature": "sig-2"}]
|
|
config = VertexGeminiConfig()
|
|
thinking_blocks = config._extract_thinking_blocks_from_parts(parts)
|
|
assert len(thinking_blocks) == 1
|
|
assert thinking_blocks[0]["thinking"] == ""
|
|
|
|
|
|
def test_thought_signature_without_thought_does_not_create_block():
|
|
"""
|
|
Test that a part with thoughtSignature but without thought=True does NOT create
|
|
a thinking block. Per Google's docs, thoughtSignature is for multi-turn context
|
|
preservation and does not indicate that the content is thinking.
|
|
"""
|
|
parts = [{"text": "Some text", "thoughtSignature": "sig-3"}]
|
|
config = VertexGeminiConfig()
|
|
thinking_blocks = config._extract_thinking_blocks_from_parts(parts)
|
|
assert thinking_blocks == []
|
|
|
|
|
|
def test_extract_thought_signatures_from_regular_parts():
|
|
"""
|
|
Test that thoughtSignatures are extracted from regular text parts (without thought=True).
|
|
This is the key feature for Gemini 3 multi-turn context preservation.
|
|
"""
|
|
parts = [{"text": "I am Gemini", "thoughtSignature": "sig-regular-123"}]
|
|
config = VertexGeminiConfig()
|
|
|
|
# Should NOT create thinking block
|
|
thinking_blocks = config._extract_thinking_blocks_from_parts(parts)
|
|
assert thinking_blocks == []
|
|
|
|
# Should extract thought signature
|
|
signatures = config._extract_thought_signatures_from_parts(parts)
|
|
assert signatures is not None
|
|
assert len(signatures) == 1
|
|
assert signatures[0] == "sig-regular-123"
|
|
|
|
|
|
def test_extract_multiple_thought_signatures():
|
|
"""
|
|
Test extraction of multiple thoughtSignatures from different parts.
|
|
"""
|
|
parts = [
|
|
{"text": "Part 1", "thoughtSignature": "sig-1"},
|
|
{"text": "Part 2", "thoughtSignature": "sig-2"},
|
|
{"text": "Part 3"}, # No signature
|
|
]
|
|
config = VertexGeminiConfig()
|
|
signatures = config._extract_thought_signatures_from_parts(parts)
|
|
|
|
assert signatures is not None
|
|
assert len(signatures) == 2
|
|
assert signatures[0] == "sig-1"
|
|
assert signatures[1] == "sig-2"
|
|
|
|
|
|
def test_round_trip_thought_signature_in_conversation():
|
|
"""
|
|
Test that thoughtSignatures are properly round-tripped through conversation history.
|
|
This ensures multi-turn context preservation works correctly.
|
|
"""
|
|
messages = [
|
|
{"role": "user", "content": "Hello"},
|
|
{
|
|
"role": "assistant",
|
|
"content": "Hi there",
|
|
"provider_specific_fields": {"thought_signatures": ["sig-round-trip-abc"]},
|
|
},
|
|
{"role": "user", "content": "How are you?"},
|
|
]
|
|
|
|
gemini_contents = _gemini_convert_messages_with_history(messages)
|
|
|
|
# Find the assistant (model) message
|
|
model_message = None
|
|
for content in gemini_contents:
|
|
if content.get("role") == "model":
|
|
model_message = content
|
|
break
|
|
|
|
assert model_message is not None
|
|
assert len(model_message["parts"]) >= 1
|
|
|
|
# Check that the text part has the thoughtSignature
|
|
text_part = model_message["parts"][0]
|
|
assert text_part["text"] == "Hi there"
|
|
assert "thoughtSignature" in text_part
|
|
assert text_part["thoughtSignature"] == "sig-round-trip-abc"
|
|
|
|
|
|
def test_round_trip_without_thought_signature_still_works():
|
|
"""
|
|
Test that messages without thoughtSignatures continue to work normally.
|
|
This ensures backward compatibility.
|
|
"""
|
|
messages = [
|
|
{"role": "user", "content": "Hello"},
|
|
{"role": "assistant", "content": "Hi there"},
|
|
{"role": "user", "content": "How are you?"},
|
|
]
|
|
|
|
gemini_contents = _gemini_convert_messages_with_history(messages)
|
|
|
|
# Find the assistant (model) message
|
|
model_message = None
|
|
for content in gemini_contents:
|
|
if content.get("role") == "model":
|
|
model_message = content
|
|
break
|
|
|
|
assert model_message is not None
|
|
assert len(model_message["parts"]) >= 1
|
|
|
|
# Check that the text part works without thoughtSignature
|
|
text_part = model_message["parts"][0]
|
|
assert text_part["text"] == "Hi there"
|
|
assert "thoughtSignature" not in text_part
|