Skip to content

Python: fix: filter google ai thought text parts#13890

Open
MukundaKatta wants to merge 1 commit intomicrosoft:mainfrom
MukundaKatta:codex/filter-google-thought-text
Open

Python: fix: filter google ai thought text parts#13890
MukundaKatta wants to merge 1 commit intomicrosoft:mainfrom
MukundaKatta:codex/filter-google-thought-text

Conversation

@MukundaKatta
Copy link
Copy Markdown

Summary:

  • skip Google AI text parts marked as thought when building chat message content
  • apply the same filtering to streaming chat message content
  • add unit tests covering thought-text filtering for both non-streaming and streaming paths

Testing:

  • attempted targeted pytest run, but this local environment is missing required repo dependencies such as openai during test loading

Closes #13710

@MukundaKatta MukundaKatta requested a review from a team as a code owner April 19, 2026 21:57
@moonbox3 moonbox3 added the python Pull requests for the Python Semantic Kernel label Apr 19, 2026
@github-actions github-actions bot changed the title fix: filter google ai thought text parts Python: fix: filter google ai thought text parts Apr 19, 2026
Copy link
Copy Markdown
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Code Review

Reviewers: 4 | Confidence: 89%

✓ Correctness

The change correctly filters out 'thought' parts (internal reasoning from Gemini models with thinking enabled) from both chat and streaming chat message content. The implementation uses getattr(part, 'thought', False) which is safe for parts that don't have the attribute. The tests are well-structured and verify the filtering behavior. No correctness issues found in the changed code. The Vertex AI service (vertex_ai_chat_completion.py) has analogous code paths that lack this filtering, but that is outside the scope of this diff.

✓ Security Reliability

The PR correctly filters out 'thought' parts (internal model reasoning) from Google AI chat completion responses, both streaming and non-streaming. The use of getattr(part, 'thought', False) is safe — Part.thought is a real attribute in the google-genai SDK that defaults to None (falsy), so non-thought parts pass through correctly. The tests adequately cover both code paths. No security or reliability issues found.

✓ Test Coverage

The PR adds filtering of Google AI 'thought' parts (model reasoning/thinking) in both _create_chat_message_content and _create_streaming_chat_message_content. Two corresponding unit tests are added that verify thought text parts are filtered while normal text parts are retained. The tests are well-structured and directly exercise the changed code paths. However, there are two minor test coverage gaps: (1) no test for the edge case where ALL parts are thought parts, resulting in empty content, and (2) no equivalent thought-filtering logic or tests for the Vertex AI chat completion service which has a parallel implementation.

✓ Design Approach

The change correctly filters out Gemini 'thought' parts (internal chain-of-thought reasoning) so they don't surface as regular TextContent items in the SK abstraction. The approach — using getattr(part, 'thought', False) before existing text/function_call checks — is safe, backward-compatible, and symmetric across both streaming and non-streaming paths. The only design-level gap is that the parallel Vertex AI connector (vertex_ai_chat_completion.py) iterates parts using 'text' in part_dict without any equivalent thought-filtering guard; if the vertexai SDK begins returning thought parts, they would leak through. No ThinkingContent type exists in SK, so silently dropping while keeping inner_content (the raw response) intact is a pragmatic and acceptable choice given the current abstraction layer.

Suggestions

  • The Vertex AI connector (vertex_ai_chat_completion.py, lines ~250–330) has parallel _create_chat_message_content and _create_streaming_chat_message_content methods that iterate over parts without filtering thought parts. If the Vertex AI SDK returns parts with a thought key, they would leak through as regular TextContent. Consider adding an equivalent guard (e.g., if part_dict.get('thought'): continue) so both connectors behave consistently when thinking models are used.

Automated review by MukundaKatta's agents

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes the Google AI (Gemini) chat connector to exclude “thought/thinking” text parts from application-visible ChatMessageContent (including streaming), addressing leakage of model reasoning into the final answer.

Changes:

  • Skip Google GenAI Part instances marked as thought when building non-streaming ChatMessageContent.
  • Apply the same filtering in the streaming message construction path.
  • Add unit tests validating thought-text filtering for both paths.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
python/semantic_kernel/connectors/ai/google/google_ai/services/google_ai_chat_completion.py Adds filtering of Part.thought parts in both non-streaming and streaming content building.
python/tests/unit/connectors/ai/google/google_ai/services/test_google_ai_chat_completion.py Adds tests to ensure thought text parts are excluded from returned message content/items.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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):
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getattr(part, "thought", False) is not safe when part can be a MagicMock (or any object whose missing attributes return truthy sentinels). In that case getattr(..., False) returns a truthy mock and this will incorrectly skip the part, breaking the existing getattr-guard tests and potentially other mock-based callers. Consider using a strict check like getattr(part, "thought", False) is True (or explicitly == True only if you avoid MagicMock’s magic methods) so only a real boolean True triggers filtering.

Suggested change
if getattr(part, "thought", False):
if getattr(part, "thought", False) is True:

Copilot uses AI. Check for mistakes.
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):
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue in streaming path: getattr(part, "thought", False) can evaluate truthy for mocks / sentinel objects even when the attribute is effectively absent, causing parts to be dropped unexpectedly. Use a strict boolean check (e.g., getattr(part, "thought", False) is True) to filter only explicit thought parts.

Suggested change
if getattr(part, "thought", False):
if getattr(part, "thought", False) is True:

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Pull requests for the Python Semantic Kernel

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: Bug: Google AI connector leaks thinking/thought text parts into ChatMessageContent

3 participants