Skip to content

Python: fix(vertex-ai): filter out thought parts from chat message content#14022

Open
rmotgi1227 wants to merge 1 commit into
microsoft:mainfrom
rmotgi1227:fix/vertex-ai-thought-leak
Open

Python: fix(vertex-ai): filter out thought parts from chat message content#14022
rmotgi1227 wants to merge 1 commit into
microsoft:mainfrom
rmotgi1227:fix/vertex-ai-thought-leak

Conversation

@rmotgi1227
Copy link
Copy Markdown

Summary

When Gemini is used via Vertex AI with include_thoughts=True, the model returns reasoning/thought parts alongside regular text parts. Thought parts have {"text": "...", "thought": True} in their to_dict() representation.

_create_chat_message_content and _create_streaming_chat_message_content in VertexAIChatCompletion checked only if "text" in part_dict, so thought text leaked into TextContent / StreamingTextContent items — exposing internal chain-of-thought reasoning as regular assistant output.

Fix

Add and not part_dict.get("thought") to the text-part guard at both call sites (non-streaming line 252, streaming line 307):

# BEFORE
if "text" in part_dict:

# AFTER
if "text" in part_dict and not part_dict.get("thought"):

This matches the fix already applied to GoogleAIChatCompletion for the same issue (#13710), and mirrors the thought_signature handling already present in the same loops.

Related

When Gemini is used via Vertex AI with include_thoughts=True, thought
parts have {"text": "...", "thought": True} in their dict representation.
Without the guard, thought text leaks into TextContent / StreamingTextContent
items in both _create_chat_message_content and _create_streaming_chat_message_content.

Add 'not part_dict.get("thought")' to the text-part check at both
call sites, mirroring the fix applied to GoogleAIChatCompletion for
the same issue (microsoft#13710).
@rmotgi1227 rmotgi1227 requested a review from a team as a code owner May 19, 2026 18:03
Copilot AI review requested due to automatic review settings May 19, 2026 18:03
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

Note

Copilot was unable to run its full agentic suite in this review.

Prevents Gemini “thought”/reasoning text parts (when include_thoughts=True) from being surfaced as normal assistant text output in the Vertex AI chat completion connector.

Changes:

  • Filter out text parts marked with thought=True in non-streaming chat message content creation.
  • Apply the same thought filtering in streaming chat message content creation.

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

for idx, part in enumerate(candidate.content.parts):
part_dict = part.to_dict()
if "text" in part_dict:
if "text" in part_dict and not part_dict.get("thought"):
for idx, part in enumerate(candidate.content.parts):
part_dict = part.to_dict()
if "text" in part_dict:
if "text" in part_dict and not part_dict.get("thought"):
@moonbox3 moonbox3 added the python Pull requests for the Python Semantic Kernel label May 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: 92%

✓ Correctness

The fix is logically correct. The guard not part_dict.get("thought") properly excludes thought parts (where to_dict() returns {"text": "..", "thought": True}) from being emitted as TextContent/StreamingTextContent, while allowing regular text parts (where the thought key is absent, so .get("thought") returns None and not None is True) to pass through. The change is minimal, precise, and mirrors the existing thought_signature handling pattern already present in the same loops. No correctness issues found.

✓ Security Reliability

The fix correctly filters out thought parts from Vertex AI responses to prevent chain-of-thought leakage. The implementation using not part_dict.get("thought") is safe—it returns None (falsy) when the key is absent, preserving regular text parts, and correctly excludes parts with thought: True. The pattern mirrors the existing thought_signature handling already present in the same loops. No additional security or reliability concerns beyond the already-flaged missing test coverage.

✓ Test Coverage

The fix correctly filters thought parts from leaking into chat message content, but lacks unit test coverage for this security-sensitive behavior. Both _create_chat_message_content and _create_streaming_chat_message_content have no tests verifying that parts with {"text": "...", "thought": True} are excluded from the output items. The existing tests in the file only cover thought_signature metadata on function call parts, not the thought-text filtering added here. No tests exist anywhere in the Google connector test suite for the "thought" key filtering pattern.

✓ Design Approach

I did not find a design-approach issue in this diff. The new guard is narrowly targeted at the described leakage path, and within the bounded context I inspected it is consistent with the existing Vertex AI handling of part metadata such as thought_signature in both non-streaming and streaming deserialization.


Automated review by rmotgi1227's agents

for idx, part in enumerate(candidate.content.parts):
part_dict = part.to_dict()
if "text" in part_dict:
if "text" in part_dict and not part_dict.get("thought"):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This security-sensitive guard needs a dedicated unit test. Please add a test to test_vertex_ai_chat_completion.py that creates a mock part with to_dict() returning {"text": "thinking..", "thought": True} alongside a normal text part ({"text": "visible"}), calls _create_chat_message_content, and asserts only the non-thought text appears in the result items.

for idx, part in enumerate(candidate.content.parts):
part_dict = part.to_dict()
if "text" in part_dict:
if "text" in part_dict and not part_dict.get("thought"):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same test coverage gap for the streaming path. Please add a test that verifies _create_streaming_chat_message_content excludes thought parts (parts whose to_dict() includes "thought": True) while still including regular text parts as StreamingTextContent.

@github-actions github-actions Bot changed the title fix(vertex-ai): filter out thought parts from chat message content Python: fix(vertex-ai): filter out thought parts from chat message content May 19, 2026
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.

3 participants