mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-17 23:51:30 +00:00
Merge f2bba9c1ba into c2c2a623c0
This commit is contained in:
commit
d7a227c825
8 changed files with 55 additions and 13 deletions
|
|
@ -158,7 +158,7 @@ def cached_messages_end_on_supported_turn(cached_messages: Sequence[AllMessageVa
|
|||
The cachedContents API rejects contents ending on a model turn, which is how it
|
||||
classifies both assistant messages and tool results, with HTTP 400
|
||||
"Requests ending with a model turn are not supported". System messages are
|
||||
extracted into system_instruction before contents are built, so the terminal
|
||||
extracted into systemInstruction before contents are built, so the terminal
|
||||
turn is the last non-system message.
|
||||
"""
|
||||
non_system_messages: Final = tuple(message for message in cached_messages if message.get("role") != "system")
|
||||
|
|
@ -206,6 +206,6 @@ def transform_openai_messages_to_gemini_context_caching(
|
|||
data["ttl"] = ttl
|
||||
|
||||
if transformed_system_messages is not None:
|
||||
data["system_instruction"] = transformed_system_messages
|
||||
data["systemInstruction"] = transformed_system_messages
|
||||
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -1218,12 +1218,12 @@ def _transform_request_body(
|
|||
generation_config["mediaResolution"] = media_resolution_value["level"]
|
||||
|
||||
data: Final = RequestBody(contents=content)
|
||||
# Vertex rejects system_instruction/tools/toolConfig alongside cachedContent.
|
||||
# Vertex rejects systemInstruction/tools/toolConfig alongside cachedContent.
|
||||
# Treat dropping these fields as a request mutation guarded by modify_params.
|
||||
can_send_cache_incompatible_fields: Final = cached_content is None or litellm.modify_params is False
|
||||
if can_send_cache_incompatible_fields:
|
||||
if system_instructions is not None:
|
||||
data["system_instruction"] = system_instructions
|
||||
data["systemInstruction"] = system_instructions
|
||||
if tools is not None:
|
||||
data["tools"] = tools
|
||||
if tool_choice is not None:
|
||||
|
|
|
|||
|
|
@ -338,7 +338,7 @@ class CachedContent(TypedDict, total=False):
|
|||
|
||||
class RequestBody(TypedDict, total=False):
|
||||
contents: Required[list[ContentType]]
|
||||
system_instruction: SystemInstructions
|
||||
systemInstruction: SystemInstructions
|
||||
tools: Tools
|
||||
toolConfig: ToolConfig
|
||||
safetySettings: list[SafetSettingsConfig]
|
||||
|
|
@ -350,7 +350,7 @@ class RequestBody(TypedDict, total=False):
|
|||
|
||||
class CachedContentRequestBody(TypedDict, total=False):
|
||||
contents: Required[list[ContentType]]
|
||||
system_instruction: SystemInstructions
|
||||
systemInstruction: SystemInstructions
|
||||
tools: Tools
|
||||
toolConfig: ToolConfig
|
||||
model: Required[str] # Format: models/{model}
|
||||
|
|
|
|||
|
|
@ -2906,7 +2906,7 @@ def test_gemini_function_call_parameter_in_messages():
|
|||
],
|
||||
},
|
||||
],
|
||||
"system_instruction": {
|
||||
"systemInstruction": {
|
||||
"parts": [{"text": "Use search for most queries."}]
|
||||
},
|
||||
"tools": [
|
||||
|
|
|
|||
|
|
@ -334,7 +334,7 @@ class TestTransformationWithTTL:
|
|||
|
||||
assert "ttl" in result
|
||||
assert result["ttl"] == "7200s"
|
||||
assert "system_instruction" in result
|
||||
assert "systemInstruction" in result
|
||||
|
||||
if custom_llm_provider == "gemini":
|
||||
assert result["model"] == "models/gemini-2.5-pro"
|
||||
|
|
|
|||
|
|
@ -1599,6 +1599,29 @@ def test_cached_messages_end_on_supported_turn():
|
|||
assert cached_messages_end_on_supported_turn([]) is False
|
||||
|
||||
|
||||
@pytest.mark.parametrize("custom_llm_provider", ["gemini", "vertex_ai", "vertex_ai_beta"])
|
||||
def test_cached_content_system_instruction_uses_canonical_rest_key(custom_llm_provider):
|
||||
"""cachedContents.create takes systemInstruction; this was the last snake_case sender in the provider."""
|
||||
from litellm.llms.vertex_ai.context_caching.transformation import (
|
||||
transform_openai_messages_to_gemini_context_caching,
|
||||
)
|
||||
|
||||
result = transform_openai_messages_to_gemini_context_caching(
|
||||
model="gemini-2.5-pro",
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a concise assistant."},
|
||||
{"role": "user", "content": "Reply with exactly: ok"},
|
||||
],
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
cache_key="test-cache-key",
|
||||
vertex_project="test_project",
|
||||
vertex_location="test_location",
|
||||
)
|
||||
|
||||
assert "system_instruction" not in result
|
||||
assert result["systemInstruction"]["parts"][0]["text"] == "You are a concise assistant."
|
||||
|
||||
|
||||
class TestCheckCachePagination:
|
||||
"""Test pagination logic in check_cache and async_check_cache methods."""
|
||||
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ def test_cached_content_respects_modify_params_for_cache_incompatible_fields():
|
|||
cached_content=cache_name,
|
||||
)
|
||||
assert result.get("cachedContent") == cache_name
|
||||
assert "system_instruction" in result
|
||||
assert "systemInstruction" in result
|
||||
assert "tools" in result
|
||||
assert "toolConfig" in result
|
||||
assert "contents" in result
|
||||
|
|
@ -139,7 +139,7 @@ def test_cached_content_respects_modify_params_for_cache_incompatible_fields():
|
|||
cached_content=cache_name,
|
||||
)
|
||||
assert result_modify_true.get("cachedContent") == cache_name
|
||||
assert "system_instruction" not in result_modify_true
|
||||
assert "systemInstruction" not in result_modify_true
|
||||
assert "tools" not in result_modify_true
|
||||
assert "toolConfig" not in result_modify_true
|
||||
assert "contents" in result_modify_true
|
||||
|
|
@ -153,13 +153,32 @@ def test_cached_content_respects_modify_params_for_cache_incompatible_fields():
|
|||
litellm_params={},
|
||||
cached_content=None,
|
||||
)
|
||||
assert "system_instruction" in result_no_cache
|
||||
assert "systemInstruction" in result_no_cache
|
||||
assert "tools" in result_no_cache
|
||||
assert "toolConfig" in result_no_cache
|
||||
finally:
|
||||
litellm.modify_params = original_modify_params
|
||||
|
||||
|
||||
@pytest.mark.parametrize("custom_llm_provider", ["gemini", "vertex_ai"])
|
||||
def test_system_instruction_uses_canonical_rest_key(custom_llm_provider):
|
||||
"""Regression for #37028: strict generateContent endpoints reject the snake_case alias."""
|
||||
result = _transform_request_body(
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a concise assistant."},
|
||||
{"role": "user", "content": "Reply with exactly: ok"},
|
||||
],
|
||||
model="gemini-2.5-pro",
|
||||
optional_params={},
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
litellm_params={},
|
||||
cached_content=None,
|
||||
)
|
||||
|
||||
assert "system_instruction" not in result
|
||||
assert result["systemInstruction"]["parts"][0]["text"] == "You are a concise assistant."
|
||||
|
||||
|
||||
# Tests for issue #14556: Labels field provider-aware filtering
|
||||
def test_google_genai_excludes_labels():
|
||||
"""Test that Google GenAI/AI Studio endpoints exclude labels when custom_llm_provider='gemini'"""
|
||||
|
|
|
|||
|
|
@ -1579,5 +1579,5 @@ def test_system_prompt_only_adds_blank_user_message():
|
|||
#########################################################
|
||||
# system message was passed in
|
||||
#########################################################
|
||||
assert len(data["system_instruction"]) == 1
|
||||
assert data["system_instruction"]["parts"][0]["text"] == SYSTEM_INSTRUCTION
|
||||
assert len(data["systemInstruction"]) == 1
|
||||
assert data["systemInstruction"]["parts"][0]["text"] == SYSTEM_INSTRUCTION
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue