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 <tan@echooo.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Krish Dholakia <krrishdholakia@gmail.com>
This commit is contained in:
Alvin Tang 2026-03-12 12:29:59 +08:00 • committed by GitHub
parent b0aa71ed9b
commit 2b7b7d3086
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 36 additions and 12 deletions

View file

@ -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":

View file

@ -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):
"""