Python: fix: filter google ai thought text parts#13890
Python: fix: filter google ai thought text parts#13890MukundaKatta wants to merge 1 commit intomicrosoft:mainfrom
Conversation
There was a problem hiding this comment.
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.thoughtis a real attribute in the google-genai SDK that defaults toNone(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_contentand_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_dictwithout any equivalent thought-filtering guard; if thevertexaiSDK begins returning thought parts, they would leak through. No ThinkingContent type exists in SK, so silently dropping while keepinginner_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_contentand_create_streaming_chat_message_contentmethods that iterate over parts without filtering thought parts. If the Vertex AI SDK returns parts with athoughtkey, 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
There was a problem hiding this comment.
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
Partinstances marked asthoughtwhen building non-streamingChatMessageContent. - 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): |
There was a problem hiding this comment.
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.
| if getattr(part, "thought", False): | |
| if getattr(part, "thought", False) is True: |
| 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): |
There was a problem hiding this comment.
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.
| if getattr(part, "thought", False): | |
| if getattr(part, "thought", False) is True: |
Summary:
Testing:
Closes #13710