Merge PR #1637: fix tool_call_id length error for GitHub Copilot provider
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
"""LiteLLM provider implementation for multi-provider support."""
|
"""LiteLLM provider implementation for multi-provider support."""
|
||||||
|
|
||||||
|
import hashlib
|
||||||
import os
|
import os
|
||||||
import secrets
|
import secrets
|
||||||
import string
|
import string
|
||||||
@@ -166,16 +167,48 @@ class LiteLLMProvider(LLMProvider):
|
|||||||
return _ANTHROPIC_EXTRA_KEYS
|
return _ANTHROPIC_EXTRA_KEYS
|
||||||
return frozenset()
|
return frozenset()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _normalize_tool_call_id(tool_call_id: Any) -> Any:
|
||||||
|
"""Normalize tool_call_id to a provider-safe 9-char alphanumeric form."""
|
||||||
|
if not isinstance(tool_call_id, str):
|
||||||
|
return tool_call_id
|
||||||
|
if len(tool_call_id) == 9 and tool_call_id.isalnum():
|
||||||
|
return tool_call_id
|
||||||
|
return hashlib.sha1(tool_call_id.encode()).hexdigest()[:9]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _sanitize_messages(messages: list[dict[str, Any]], extra_keys: frozenset[str] = frozenset()) -> list[dict[str, Any]]:
|
def _sanitize_messages(messages: list[dict[str, Any]], extra_keys: frozenset[str] = frozenset()) -> list[dict[str, Any]]:
|
||||||
"""Strip non-standard keys and ensure assistant messages have a content key."""
|
"""Strip non-standard keys and ensure assistant messages have a content key."""
|
||||||
allowed = _ALLOWED_MSG_KEYS | extra_keys
|
allowed = _ALLOWED_MSG_KEYS | extra_keys
|
||||||
sanitized = []
|
sanitized = []
|
||||||
|
id_map: dict[str, str] = {}
|
||||||
|
|
||||||
|
def map_id(value: Any) -> Any:
|
||||||
|
if not isinstance(value, str):
|
||||||
|
return value
|
||||||
|
return id_map.setdefault(value, LiteLLMProvider._normalize_tool_call_id(value))
|
||||||
|
|
||||||
for msg in messages:
|
for msg in messages:
|
||||||
clean = {k: v for k, v in msg.items() if k in allowed}
|
clean = {k: v for k, v in msg.items() if k in allowed}
|
||||||
# Strict providers require "content" even when assistant only has tool_calls
|
# Strict providers require "content" even when assistant only has tool_calls
|
||||||
if clean.get("role") == "assistant" and "content" not in clean:
|
if clean.get("role") == "assistant" and "content" not in clean:
|
||||||
clean["content"] = None
|
clean["content"] = None
|
||||||
|
|
||||||
|
# Keep assistant tool_calls[].id and tool tool_call_id in sync after
|
||||||
|
# shortening, otherwise strict providers reject the broken linkage.
|
||||||
|
if isinstance(clean.get("tool_calls"), list):
|
||||||
|
normalized_tool_calls = []
|
||||||
|
for tc in clean["tool_calls"]:
|
||||||
|
if not isinstance(tc, dict):
|
||||||
|
normalized_tool_calls.append(tc)
|
||||||
|
continue
|
||||||
|
tc_clean = dict(tc)
|
||||||
|
tc_clean["id"] = map_id(tc_clean.get("id"))
|
||||||
|
normalized_tool_calls.append(tc_clean)
|
||||||
|
clean["tool_calls"] = normalized_tool_calls
|
||||||
|
|
||||||
|
if "tool_call_id" in clean and clean["tool_call_id"]:
|
||||||
|
clean["tool_call_id"] = map_id(clean["tool_call_id"])
|
||||||
sanitized.append(clean)
|
sanitized.append(clean)
|
||||||
return sanitized
|
return sanitized
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user