From ca94cff3141e70774183ac65f845371bd6e4c6bc Mon Sep 17 00:00:00 2001 From: mukunda katta Date: Sun, 19 Apr 2026 14:56:54 -0700 Subject: [PATCH 1/2] fix: filter google ai thought text parts --- .../services/google_ai_chat_completion.py | 4 ++ .../test_google_ai_chat_completion.py | 67 ++++++++++++++++++- 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/python/semantic_kernel/connectors/ai/google/google_ai/services/google_ai_chat_completion.py b/python/semantic_kernel/connectors/ai/google/google_ai/services/google_ai_chat_completion.py index 22422c331d2b..431eb72cfa23 100644 --- a/python/semantic_kernel/connectors/ai/google/google_ai/services/google_ai_chat_completion.py +++ b/python/semantic_kernel/connectors/ai/google/google_ai/services/google_ai_chat_completion.py @@ -302,6 +302,8 @@ def _create_chat_message_content( items: list[CMC_ITEM_TYPES] = [] if candidate.content and candidate.content.parts: for idx, part in enumerate(candidate.content.parts): + if getattr(part, "thought", False): + continue if part.text: items.append(TextContent(text=part.text, inner_content=response, metadata=response_metadata)) elif part.function_call: @@ -357,6 +359,8 @@ def _create_streaming_chat_message_content( items: list[STREAMING_ITEM_TYPES] = [] if candidate.content and candidate.content.parts: for idx, part in enumerate(candidate.content.parts): + if getattr(part, "thought", False): + continue if part.text: items.append( StreamingTextContent( diff --git a/python/tests/unit/connectors/ai/google/google_ai/services/test_google_ai_chat_completion.py b/python/tests/unit/connectors/ai/google/google_ai/services/test_google_ai_chat_completion.py index a0dc2e786195..cc87b0364e13 100644 --- a/python/tests/unit/connectors/ai/google/google_ai/services/test_google_ai_chat_completion.py +++ b/python/tests/unit/connectors/ai/google/google_ai/services/test_google_ai_chat_completion.py @@ -5,7 +5,15 @@ import pytest from google.genai import Client from google.genai.models import AsyncModels -from google.genai.types import Content, GenerateContentConfigDict +from google.genai.types import ( + Candidate, + Content, + FinishReason as GoogleFinishReason, + GenerateContentConfigDict, + GenerateContentResponse, + GenerateContentResponseUsageMetadata, + Part, +) from semantic_kernel.connectors.ai.function_choice_behavior import FunctionChoiceBehavior from semantic_kernel.connectors.ai.google.google_ai.google_ai_prompt_execution_settings import ( @@ -259,6 +267,63 @@ async def test_google_ai_chat_completion_with_function_choice_behavior_no_tool_c # endregion chat completion +def test_google_ai_chat_completion_filters_thought_text_parts(google_ai_unit_test_env) -> None: + google_ai_chat_completion = GoogleAIChatCompletion() + + candidate = Candidate() + candidate.index = 0 + candidate.content = Content(role="model", parts=[]) + candidate.finish_reason = GoogleFinishReason.STOP + + thinking_part = Part.from_text(text="internal reasoning") + thinking_part.thought = True # type: ignore[attr-defined] + answer_part = Part.from_text(text="final answer") + + candidate.content.parts.extend([thinking_part, answer_part]) + + response = GenerateContentResponse() + response.candidates = [candidate] + response.usage_metadata = GenerateContentResponseUsageMetadata( + prompt_token_count=0, + cached_content_token_count=0, + candidates_token_count=0, + total_token_count=0, + ) + + message = google_ai_chat_completion._create_chat_message_content(response, candidate) + + assert message.content == "final answer" + assert len(message.items) == 1 + + +def test_google_ai_streaming_chat_completion_filters_thought_text_parts(google_ai_unit_test_env) -> None: + google_ai_chat_completion = GoogleAIChatCompletion() + + candidate = Candidate() + candidate.index = 0 + candidate.content = Content(role="model", parts=[]) + candidate.finish_reason = GoogleFinishReason.STOP + + thinking_part = Part.from_text(text="internal reasoning") + thinking_part.thought = True # type: ignore[attr-defined] + answer_part = Part.from_text(text="streamed answer") + candidate.content.parts.extend([thinking_part, answer_part]) + + response = GenerateContentResponse() + response.candidates = [candidate] + response.usage_metadata = GenerateContentResponseUsageMetadata( + prompt_token_count=0, + cached_content_token_count=0, + candidates_token_count=0, + total_token_count=0, + ) + + message = google_ai_chat_completion._create_streaming_chat_message_content(response, candidate) + + assert message.content == "streamed answer" + assert len(message.items) == 1 + + # region streaming chat completion From 7b5162ca489225fe8686bddf574b4f0834fa8d94 Mon Sep 17 00:00:00 2001 From: Mukunda Rao Katta Date: Wed, 22 Apr 2026 15:23:39 -0700 Subject: [PATCH 2/2] fix(google-ai): use strict 'is True' when filtering thought parts Per @Copilot review: getattr(part, 'thought', False) returns a truthy MagicMock when the attribute is missing on a mock, which unexpectedly drops parts in mock-based tests. Tighten to 'is True' so only a real boolean True triggers the filter (both the non-streaming and streaming paths). --- .../ai/google/google_ai/services/google_ai_chat_completion.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/semantic_kernel/connectors/ai/google/google_ai/services/google_ai_chat_completion.py b/python/semantic_kernel/connectors/ai/google/google_ai/services/google_ai_chat_completion.py index 431eb72cfa23..576058625023 100644 --- a/python/semantic_kernel/connectors/ai/google/google_ai/services/google_ai_chat_completion.py +++ b/python/semantic_kernel/connectors/ai/google/google_ai/services/google_ai_chat_completion.py @@ -302,7 +302,7 @@ def _create_chat_message_content( items: list[CMC_ITEM_TYPES] = [] if candidate.content and candidate.content.parts: for idx, part in enumerate(candidate.content.parts): - if getattr(part, "thought", False): + if getattr(part, "thought", False) is True: continue if part.text: items.append(TextContent(text=part.text, inner_content=response, metadata=response_metadata)) @@ -359,7 +359,7 @@ def _create_streaming_chat_message_content( items: list[STREAMING_ITEM_TYPES] = [] if candidate.content and candidate.content.parts: for idx, part in enumerate(candidate.content.parts): - if getattr(part, "thought", False): + if getattr(part, "thought", False) is True: continue if part.text: items.append(