From 79d3dde1e6b2c49e83514f293a22e7bf3d3edd41 Mon Sep 17 00:00:00 2001 From: Krish Dholakia Date: Tue, 15 Apr 2025 19:21:00 -0700 Subject: [PATCH] =?UTF-8?q?Revert=20"fix=20#9783:=20Retain=20schema=20fiel?= =?UTF-8?q?d=20ordering=20for=20google=20gemini=20and=20vertex=20=E2=80=A6?= =?UTF-8?q?"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit e3729f9855749fb6e5ab97675681e70486ce45fb. --- litellm/llms/vertex_ai/common_utils.py | 38 +---- .../vertex_and_google_ai_studio_gemini.py | 7 +- ...test_vertex_and_google_ai_studio_gemini.py | 159 ------------------ 3 files changed, 6 insertions(+), 198 deletions(-) diff --git a/litellm/llms/vertex_ai/common_utils.py b/litellm/llms/vertex_ai/common_utils.py index 9f7b7f49a7..be8e4749dd 100644 --- a/litellm/llms/vertex_ai/common_utils.py +++ b/litellm/llms/vertex_ai/common_utils.py @@ -165,18 +165,9 @@ def _check_text_in_content(parts: List[PartType]) -> bool: return has_text_param -def _build_vertex_schema(parameters: dict, add_property_ordering: bool = False): +def _build_vertex_schema(parameters: dict): """ This is a modified version of https://github.com/google-gemini/generative-ai-python/blob/8f77cc6ac99937cd3a81299ecf79608b91b06bbb/google/generativeai/types/content_types.py#L419 - - Updates the input parameters, removing extraneous fields, adjusting types, unwinding $defs, and adding propertyOrdering if specified, returning the updated parameters. - - Parameters: - parameters: dict - the json schema to build from - add_property_ordering: bool - whether to add propertyOrdering to the schema. This is only applicable to schemas for structured outputs. See - set_schema_property_ordering for more details. - Returns: - parameters: dict - the input parameters, modified in place """ # Get valid fields from Schema TypedDict valid_schema_fields = set(get_type_hints(Schema).keys()) @@ -195,31 +186,8 @@ def _build_vertex_schema(parameters: dict, add_property_ordering: bool = False): add_object_type(parameters) # Postprocessing # Filter out fields that don't exist in Schema - parameters = filter_schema_fields(parameters, valid_schema_fields) - - if add_property_ordering: - set_schema_property_ordering(parameters) - return parameters - - -def set_schema_property_ordering(schema: Dict[str, Any]) -> Dict[str, Any]: - """ - vertex ai and generativeai apis order output of fields alphabetically, unless you specify the order. - python dicts retain order, so we just use that. Note that this field only applies to structured outputs, and not tools. - Function tools are not afflicted by the same alphabetical ordering issue, (the order of keys returned seems to be arbitrary, up to the model) - https://cloud.google.com/vertex-ai/docs/reference/rest/v1/projects.locations.cachedContents#Schema.FIELDS.property_ordering - """ - if "properties" in schema and isinstance(schema["properties"], dict): - # retain propertyOrdering as an escape hatch if user already specifies it - if "propertyOrdering" not in schema: - schema["propertyOrdering"] = [k for k, v in schema["properties"].items()] - for k, v in schema["properties"].items(): - set_schema_property_ordering(v) - if "items" in schema: - set_schema_property_ordering(schema["items"]) - return schema - - + filtered_parameters = filter_schema_fields(parameters, valid_schema_fields) + return filtered_parameters def filter_schema_fields( diff --git a/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py b/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py index 3a58bb2c6d..c57090093b 100644 --- a/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py +++ b/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py @@ -207,7 +207,7 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): "extra_headers", "seed", "logprobs", - "top_logprobs", + "top_logprobs", # Added this to list of supported openAI params "modalities", ] @@ -313,10 +313,9 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): if isinstance(old_schema, list): for item in old_schema: if isinstance(item, dict): - item = _build_vertex_schema(parameters=item, add_property_ordering=True) - + item = _build_vertex_schema(parameters=item) elif isinstance(old_schema, dict): - old_schema = _build_vertex_schema(parameters=old_schema, add_property_ordering=True) + old_schema = _build_vertex_schema(parameters=old_schema) return old_schema def apply_response_schema_transformation(self, value: dict, optional_params: dict): diff --git a/tests/litellm/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py b/tests/litellm/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py index 41a37e2e57..d2169c299e 100644 --- a/tests/litellm/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py +++ b/tests/litellm/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py @@ -9,8 +9,6 @@ from litellm.llms.vertex_ai.gemini.vertex_and_google_ai_studio_gemini import ( VertexGeminiConfig, ) from litellm.types.utils import ChoiceLogprobs -from pydantic import BaseModel -from typing import List, cast def test_top_logprobs(): @@ -64,160 +62,3 @@ def test_get_model_name_from_gemini_spec_model(): model = "gemini/ft-uuid-123" result = VertexGeminiConfig._get_model_name_from_gemini_spec_model(model) assert result == "ft-uuid-123" - - - -def test_vertex_ai_response_schema_dict(): - v = VertexGeminiConfig() - transformed_request = v.map_openai_params( - non_default_params={ - "messages": [{"role": "user", "content": "Hello, world!"}], - "response_format": { - "type": "json_schema", - "json_schema": { - "name": "math_reasoning", - "schema": { - "type": "object", - "properties": { - "steps": { - "type": "array", - "items": { - "type": "object", - "properties": { - "thought": {"type": "string"}, - "output": {"type": "string"}, - }, - "required": ["thought", "output"], - "additionalProperties": False, - }, - }, - "final_answer": {"type": "string"}, - }, - "required": ["steps", "final_answer"], - "additionalProperties": False, - }, - "strict": False, - }, - }, - }, - optional_params={}, - model="gemini-2.0-flash-lite", - drop_params=False, - ) - - schema = transformed_request["response_schema"] - # should add propertyOrdering - assert schema["propertyOrdering"] == ["steps", "final_answer"] - # should add propertyOrdering (recursively, including array items) - assert schema["properties"]["steps"]["items"]["propertyOrdering"] == [ - "thought", - "output", - ] - # should strip strict and additionalProperties - assert "strict" not in schema - assert "additionalProperties" not in schema - # validate the whole thing to catch regressions - assert transformed_request["response_schema"] == { - "type": "object", - "properties": { - "steps": { - "type": "array", - "items": { - "type": "object", - "properties": { - "thought": {"type": "string"}, - "output": {"type": "string"}, - }, - "required": ["thought", "output"], - "propertyOrdering": ["thought", "output"], - }, - }, - "final_answer": {"type": "string"}, - }, - "required": ["steps", "final_answer"], - "propertyOrdering": ["steps", "final_answer"], - } - - -class MathReasoning(BaseModel): - steps: List["Step"] - final_answer: str - - -class Step(BaseModel): - thought: str - output: str - - -def test_vertex_ai_response_schema_defs(): - v = VertexGeminiConfig() - - schema = cast(dict, v.get_json_schema_from_pydantic_object(MathReasoning)) - - # pydantic conversion by default adds $defs to the schema, make sure this is still the case, otherwise this test isn't really testing anything - assert "$defs" in schema["json_schema"]["schema"] - - transformed_request = v.map_openai_params( - non_default_params={ - "messages": [{"role": "user", "content": "Hello, world!"}], - "response_format": schema, - }, - optional_params={}, - model="gemini-2.0-flash-lite", - drop_params=False, - ) - - assert "$defs" not in transformed_request["response_schema"] - assert transformed_request["response_schema"] == { - "title": "MathReasoning", - "type": "object", - "properties": { - "steps": { - "title": "Steps", - "type": "array", - "items": { - "title": "Step", - "type": "object", - "properties": { - "thought": {"title": "Thought", "type": "string"}, - "output": {"title": "Output", "type": "string"}, - }, - "required": ["thought", "output"], - "propertyOrdering": ["thought", "output"], - }, - }, - "final_answer": {"title": "Final Answer", "type": "string"}, - }, - "required": ["steps", "final_answer"], - "propertyOrdering": ["steps", "final_answer"], - } - - -def test_vertex_ai_retain_property_ordering(): - v = VertexGeminiConfig() - transformed_request = v.map_openai_params( - non_default_params={ - "messages": [{"role": "user", "content": "Hello, world!"}], - "response_format": { - "type": "json_schema", - "json_schema": { - "name": "math_reasoning", - "schema": { - "type": "object", - "properties": { - "output": {"type": "string"}, - "thought": {"type": "string"}, - }, - "propertyOrdering": ["thought", "output"], - }, - }, - }, - }, - optional_params={}, - model="gemini-2.0-flash-lite", - drop_params=False, - ) - - schema = transformed_request["response_schema"] - # should leave existing value alone, despite dictionary ordering - assert schema["propertyOrdering"] == ["thought", "output"]