diff --git a/services/ai/providers/anthropic_message_adapter.py b/services/ai/providers/anthropic_message_adapter.py index ba55d145..bd1c2e7c 100644 --- a/services/ai/providers/anthropic_message_adapter.py +++ b/services/ai/providers/anthropic_message_adapter.py @@ -33,6 +33,20 @@ class OmniSearchResultBlockParam(SearchResultBlockParam, total=False): type AnthropicMessageContent = str | Iterable[OmniContentBlockParam] +def _is_empty_text_block(block: ContentBlockParam) -> bool: + """Return True if *block* is a text content block with empty/whitespace-only text.""" + return bool( + block.get("type") == "text" and not (block.get("text") or "").strip() + ) + + +def _drain_empty_text_blocks(blocks: list[ContentBlockParam]) -> bool: + """Remove empty text blocks in-place; return True if any were removed.""" + before = len(blocks) + blocks[:] = [b for b in blocks if not _is_empty_text_block(b)] + return len(blocks) < before + + def extract_text_document(block: DocumentBlockParam) -> str | None: """Convert a text-backed document block into provider-neutral text.""" if block.get("type") != "document": @@ -70,7 +84,9 @@ def _build_content_for_api( ) -> str | list[ContentBlockParam]: if isinstance(content, str): return content - return [_build_block_for_api(block) for block in content] + blocks = [_build_block_for_api(block) for block in content] + _drain_empty_text_blocks(blocks) + return blocks def _build_block_for_api(block: OmniContentBlockParam) -> ContentBlockParam: @@ -104,7 +120,9 @@ def _build_tool_result_content_for_api( ) -> str | list[ToolResultContentBlockParam]: if isinstance(content, str): return content - return [_build_tool_result_content_block_for_api(block) for block in content] + blocks = [_build_tool_result_content_block_for_api(block) for block in content] + _drain_empty_text_blocks(blocks) + return blocks def _build_tool_result_content_block_for_api( diff --git a/services/ai/providers/openai.py b/services/ai/providers/openai.py index 9fe42367..c9965d08 100644 --- a/services/ai/providers/openai.py +++ b/services/ai/providers/openai.py @@ -338,7 +338,9 @@ def _convert_messages(self, messages: list[dict[str, Any]]) -> list[dict[str, An block_type = block.get("type") if block_type == "text": - text_parts.append(block.get("text", "")) + text = block.get("text", "") + if text: + text_parts.append(text) elif block_type == "document" and role == "user": document_text = extract_text_document(block) if document_text is not None: @@ -363,7 +365,9 @@ def _convert_messages(self, messages: list[dict[str, Any]]) -> list[dict[str, An for rb in result_content: if isinstance(rb, dict): if rb.get("type") == "text": - parts.append(rb.get("text", "")) + t = rb.get("text", "") + if t: + parts.append(t) elif rb.get("type") == "search_result": title = rb.get("title", "") source = rb.get("source", "") diff --git a/services/ai/providers/openai_compatible.py b/services/ai/providers/openai_compatible.py index 3c2fd2b0..af554a73 100644 --- a/services/ai/providers/openai_compatible.py +++ b/services/ai/providers/openai_compatible.py @@ -208,7 +208,8 @@ def _convert_messages_to_openai( continue if rb.get("type") == "text": rb = cast(TextBlockParam, rb) - parts.append(rb["text"]) + if rb["text"]: + parts.append(rb["text"]) elif rb.get("type") == "search_result": title = rb.get("title", "") source = rb.get("source", "")