refactor(providers): move empty content sanitization to base class

This commit is contained in:
Re-bin
2026-02-22 18:26:42 +00:00
parent 2f7835a301
commit b653183bb0
3 changed files with 42 additions and 45 deletions

View File

@@ -39,6 +39,46 @@ class LLMProvider(ABC):
def __init__(self, api_key: str | None = None, api_base: str | None = None):
self.api_key = api_key
self.api_base = api_base
@staticmethod
def _sanitize_empty_content(messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
"""Replace empty text content that causes provider 400 errors.
Empty content can appear when MCP tools return nothing. Most providers
reject empty-string content or empty text blocks in list content.
"""
result: list[dict[str, Any]] = []
for msg in messages:
content = msg.get("content")
if isinstance(content, str) and not content:
clean = dict(msg)
clean["content"] = None if (msg.get("role") == "assistant" and msg.get("tool_calls")) else "(empty)"
result.append(clean)
continue
if isinstance(content, list):
filtered = [
item for item in content
if not (
isinstance(item, dict)
and item.get("type") in ("text", "input_text", "output_text")
and not item.get("text")
)
]
if len(filtered) != len(content):
clean = dict(msg)
if filtered:
clean["content"] = filtered
elif msg.get("role") == "assistant" and msg.get("tool_calls"):
clean["content"] = None
else:
clean["content"] = "(empty)"
result.append(clean)
continue
result.append(msg)
return result
@abstractmethod
async def chat(

View File

@@ -21,7 +21,7 @@ class CustomProvider(LLMProvider):
model: str | None = None, max_tokens: int = 4096, temperature: float = 0.7) -> LLMResponse:
kwargs: dict[str, Any] = {
"model": model or self.default_model,
"messages": self._prevent_empty_text_blocks(messages),
"messages": self._sanitize_empty_content(messages),
"max_tokens": max(1, max_tokens),
"temperature": temperature,
}
@@ -50,46 +50,3 @@ class CustomProvider(LLMProvider):
def get_default_model(self) -> str:
return self.default_model
@staticmethod
def _prevent_empty_text_blocks(messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
"""Filter empty text content blocks that cause provider 400 errors.
When MCP tools return empty content, messages may contain empty-string
text blocks. Most providers (OpenAI-compatible) reject these with 400.
This method filters them out before sending to the API.
"""
patched: list[dict[str, Any]] = []
for msg in messages:
content = msg.get("content")
# Empty string content
if isinstance(content, str) and content == "":
clean = dict(msg)
if msg.get("role") == "assistant" and msg.get("tool_calls"):
clean["content"] = None
else:
clean["content"] = "(empty)"
patched.append(clean)
continue
# List content — filter out empty text items
if isinstance(content, list):
filtered = [
item for item in content
if not (isinstance(item, dict)
and item.get("type") in {"text", "input_text", "output_text"}
and item.get("text") == "")
]
if filtered != content:
clean = dict(msg)
if filtered:
clean["content"] = filtered
elif msg.get("role") == "assistant" and msg.get("tool_calls"):
clean["content"] = None
else:
clean["content"] = "(empty)"
patched.append(clean)
continue
patched.append(msg)
return patched

View File

@@ -196,7 +196,7 @@ class LiteLLMProvider(LLMProvider):
kwargs: dict[str, Any] = {
"model": model,
"messages": self._sanitize_messages(messages),
"messages": self._sanitize_messages(self._sanitize_empty_content(messages)),
"max_tokens": max_tokens,
"temperature": temperature,
}