mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-12 23:01:41 +00:00
fix: handle Pydantic models consistently in message transformation
- Apply model_dump() to Pydantic messages in tool_calls_map building loop, not just in the main transformation loop (fixes incomplete map bug) - Handle Pydantic tool_call objects in _convert_assistant_tool_message, preventing silent drops when tool_calls are BaseModel instances - Document that non-text content parts (images) are intentionally dropped since Snowflake's content_list only supports plain strings - Filter empty strings from multipart content flattening - Fall back to 'name' field on tool message when tool_call_id lookup fails (matches OpenAI message format where name can be on the message itself) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c55192d74c
commit
c2231ae225
1 changed files with 25 additions and 15 deletions
|
|
@ -174,13 +174,16 @@ class SnowflakeConfig(SnowflakeBaseConfig, OpenAIGPTConfig):
|
|||
Snowflake transformation is synchronous and doesn't require async operations
|
||||
like image URL downloads that the parent class handles.
|
||||
"""
|
||||
# Build a map of tool_call_id -> tool_call for looking up function names
|
||||
# Build a map of tool_call_id -> tool_call for looking up function names.
|
||||
# Must handle both dict and Pydantic BaseModel messages/tool_calls.
|
||||
tool_calls_map: Dict[str, Dict[str, Any]] = {}
|
||||
for message in messages:
|
||||
if isinstance(message, dict) and message.get("role") == "assistant":
|
||||
for tc in message.get("tool_calls") or []:
|
||||
if isinstance(tc, dict):
|
||||
tool_calls_map[tc.get("id", "")] = tc
|
||||
msg = message.model_dump() if isinstance(message, BaseModel) else message
|
||||
if isinstance(msg, dict) and msg.get("role") == "assistant":
|
||||
for tc in msg.get("tool_calls") or []:
|
||||
tc_dict = tc.model_dump() if isinstance(tc, BaseModel) else tc
|
||||
if isinstance(tc_dict, dict):
|
||||
tool_calls_map[tc_dict.get("id", "")] = tc_dict
|
||||
|
||||
transformed: List[Dict[str, Any]] = []
|
||||
pending_tool_messages: List[Dict[str, Any]] = []
|
||||
|
|
@ -249,18 +252,23 @@ class SnowflakeConfig(SnowflakeBaseConfig, OpenAIGPTConfig):
|
|||
"""
|
||||
content_list: List[Dict[str, Any]] = []
|
||||
|
||||
# Add text content if present
|
||||
# Add text content if present.
|
||||
# Note: Non-text parts (e.g., images) are intentionally dropped because
|
||||
# Snowflake's content_list text blocks only support plain strings.
|
||||
text_content = message.get("content")
|
||||
if isinstance(text_content, list):
|
||||
# Flatten multipart content to a single string
|
||||
# Flatten to text only; filter out empty strings from non-text parts
|
||||
text_content = " ".join(
|
||||
part.get("text", "") for part in text_content if isinstance(part, dict)
|
||||
part.get("text", "")
|
||||
for part in text_content
|
||||
if isinstance(part, dict) and part.get("text")
|
||||
)
|
||||
if text_content:
|
||||
content_list.append({"type": "text", "text": text_content})
|
||||
|
||||
# Add tool_use blocks
|
||||
for tool_call in message.get("tool_calls") or []:
|
||||
# Add tool_use blocks. Handle both dict and Pydantic BaseModel tool_calls.
|
||||
for raw_tc in message.get("tool_calls") or []:
|
||||
tool_call = raw_tc.model_dump() if isinstance(raw_tc, BaseModel) else raw_tc
|
||||
if isinstance(tool_call, dict):
|
||||
function = tool_call.get("function", {})
|
||||
# Parse arguments from JSON string to dict
|
||||
|
|
@ -311,11 +319,13 @@ class SnowflakeConfig(SnowflakeBaseConfig, OpenAIGPTConfig):
|
|||
tool_call_id = tool_msg.get("tool_call_id", "")
|
||||
tool_call = tool_calls_map.get(tool_call_id)
|
||||
if tool_call is None:
|
||||
litellm.utils.verbose_logger.warning(
|
||||
f"Snowflake: tool_call_id '{tool_call_id}' not found in prior "
|
||||
"assistant messages; function name will be empty."
|
||||
)
|
||||
function_name = ""
|
||||
# Fall back to 'name' field on the tool message itself (OpenAI format)
|
||||
function_name = tool_msg.get("name", "")
|
||||
if not function_name:
|
||||
litellm.utils.verbose_logger.warning(
|
||||
f"Snowflake: tool_call_id '{tool_call_id}' not found in prior "
|
||||
"assistant messages and no 'name' field; function name will be empty."
|
||||
)
|
||||
else:
|
||||
function = tool_call.get("function", {})
|
||||
function_name = function.get("name", "")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue