From 2b7b7d30860a7cacd12ac754b6cf1278171bbefe Mon Sep 17 00:00:00 2001 From: Alvin Tang <104285249+alvinttang@users.noreply.github.com> Date: Thu, 12 Mar 2026 12:29:59 +0800 Subject: [PATCH] fix(snowflake): transform string tool_choice to object format (#23318) Snowflake's Cortex LLM API (like Anthropic) requires tool_choice as an object with a "type" field, not as a bare string. Passing tool_choice="auto" (or "required"/"none") results in error 390142 "invalid payload". This fix transforms OpenAI string tool_choice values to the Snowflake object format: - "auto" -> {"type": "auto"} - "required" -> {"type": "any"} (Snowflake/Anthropic convention) - "none" -> {"type": "none"} The dict-to-dict transformation for specific function tool choices ({"type": "function", "function": {"name": "..."}} -> {"type": "tool", "name": [...]}) remains unchanged. Fixes #23284 Co-authored-by: gambletan Co-authored-by: Claude Opus 4.6 Co-authored-by: Krish Dholakia --- litellm/llms/snowflake/chat/transformation.py | 29 ++++++++++++++----- .../test_snowflake_chat_transformation.py | 19 ++++++++---- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/litellm/llms/snowflake/chat/transformation.py b/litellm/llms/snowflake/chat/transformation.py index e11cab4138d..3e590680a75 100644 --- a/litellm/llms/snowflake/chat/transformation.py +++ b/litellm/llms/snowflake/chat/transformation.py @@ -219,17 +219,32 @@ class SnowflakeConfig(SnowflakeBaseConfig, OpenAIGPTConfig): tool_choice: Tool choice in OpenAI format (str or dict) Returns: - Tool choice in Snowflake format (always an object) + Tool choice in Snowflake format (always an object, never a string) - OpenAI format (string): "auto", "required", "none" - OpenAI format (object): {"type": "function", "function": {"name": "get_weather"}} + OpenAI format (string): + "auto", "required", "none" - Snowflake format (string values become objects): {"type": "auto"} - Snowflake format (specific tool): {"type": "tool", "name": ["get_weather"]} + OpenAI format (dict): + {"type": "function", "function": {"name": "get_weather"}} + + Snowflake format: + {"type": "auto"} / {"type": "any"} / {"type": "none"} + {"type": "tool", "name": ["get_weather"]} + + Snowflake's API (like Anthropic) requires tool_choice as an object + with a "type" field, not as a bare string. """ if isinstance(tool_choice, str): - # Snowflake requires object format: {"type": "auto"} not string "auto" - return {"type": tool_choice} + # Snowflake requires object format, not string. + # Map OpenAI string values to Snowflake object format. + # "required" maps to "any" (Snowflake/Anthropic convention). + _type_map = { + "auto": "auto", + "required": "any", + "none": "none", + } + mapped_type = _type_map.get(tool_choice, tool_choice) + return {"type": mapped_type} if isinstance(tool_choice, dict): if tool_choice.get("type") == "function": diff --git a/tests/test_litellm/llms/snowflake/chat/test_snowflake_chat_transformation.py b/tests/test_litellm/llms/snowflake/chat/test_snowflake_chat_transformation.py index 3c1fa52cb09..5bb4942dde6 100644 --- a/tests/test_litellm/llms/snowflake/chat/test_snowflake_chat_transformation.py +++ b/tests/test_litellm/llms/snowflake/chat/test_snowflake_chat_transformation.py @@ -107,12 +107,19 @@ class TestSnowflakeToolTransformation: """ Test that string tool_choice values are transformed to Snowflake object format. - Snowflake requires tool_choice to be an object, not a string. - Ref: https://docs.snowflake.com/en/developer-guide/snowflake-rest-api/reference/cortex-inference#post--api-v2-cortex-inference-complete-req-body-schema + Snowflake's API (like Anthropic) requires tool_choice as an object + with a "type" field, not as a bare string. OpenAI's "required" maps + to Snowflake's "any". """ config = SnowflakeConfig() - for value in ["auto", "required", "none"]: + expected_mappings = { + "auto": {"type": "auto"}, + "required": {"type": "any"}, + "none": {"type": "none"}, + } + + for value, expected in expected_mappings.items(): optional_params = {"tool_choice": value} transformed_request = config.transform_request( @@ -123,8 +130,10 @@ class TestSnowflakeToolTransformation: headers={}, ) - # Snowflake requires object format: {"type": "auto"} not string "auto" - assert transformed_request["tool_choice"] == {"type": value} + assert transformed_request["tool_choice"] == expected, ( + f"tool_choice='{value}' should be transformed to {expected}, " + f"got {transformed_request['tool_choice']}" + ) def test_transform_response_with_tool_calls(self): """