Skip to content

Conversation

stephentoub
Copy link
Member

@stephentoub stephentoub commented Sep 9, 2025

Fixes #6510

I considered multiple approaches, including having a separate content type dedicated to encrypted/redacted/protected data, but ended up just extending the existing TextReasoningContent with a new optional ProtectedData property.

Anthropic has thinking content, which is human-readable text that's accompanied by an opaque "signature" blob: that's representable with the thinking as Text and the signature as ProtectedData. It also has redacted thinking content, which is just an opaque data string, which we can represent with empty Text and data as ProtectedData.

OpenAI lets users of the Responses API opt-in to including an encrypted version of the reasoning trace in their output, which is most useful when the developer also opts-out of storing the responses in the service and thus needs to roundtrip the data back. By default, reasoning output only includes human-readable text, but if opted into encrypted reasoning, the output items will typically include both the summary human-readable text and the encrypted data. That can be modeled using Text for the summary (as we do today) and ProtectedData for the encrypted content.

AWS Bedrock exposes reasoning via a ReasoningContentBlock, which has both reasoningText (human readable) and reasoningContent (opaque encrypted content). However, it'll only ever use one of them in each instance, so we can end up with either Text being non-empty or ProtectedData being non-null.

Gemini has a Part that has both a thought (human readable) and thoughtSignature (opaque data), which we can represent as Text and ProtectedData, respectively.

The one thing gnawing at me is that we have Text right now as always returning a non-null string: if you give it null, it gives you back empty. That means the only way we have to distinguish "only ProtectedData" from "Text and ProtectedData" is if Text is empty. Other than bugs, I don't think there are real situations where one of these services sends back empty Text, but if we find that can happen, we might either need to take a (non-binary breaking) change to have Text be nullable, or add some sort of additional property to the instance that specifies what kind of data it carries.

Microsoft Reviewers: Open in CodeFlow

@github-actions github-actions bot added the area-ai Microsoft.Extensions.AI libraries label Sep 9, 2025
@stephentoub
Copy link
Member Author

cc: @tghamm

@stephentoub stephentoub marked this pull request as ready for review September 10, 2025 17:14
@Copilot Copilot AI review requested due to automatic review settings September 10, 2025 17:14
@stephentoub stephentoub requested a review from a team as a code owner September 10, 2025 17:14
Copy link
Contributor

@Copilot 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

This PR adds a new ProtectedData property to TextReasoningContent to support provider-specific opaque data blobs that need to be roundtripped but aren't intended for human consumption. This enables support for encrypted reasoning traces, signature blobs, and redacted content from various AI providers like OpenAI, Anthropic, AWS Bedrock, and Gemini.

Key changes:

  • Adds nullable ProtectedData string property to TextReasoningContent
  • Updates OpenAI integration to handle encrypted content in reasoning responses
  • Modifies content coalescing logic to prevent merging when protected data is present

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
TextReasoningContent.cs Adds the new ProtectedData property with comprehensive documentation
OpenAIResponsesChatClient.cs Updates OpenAI integration to map encrypted content to/from the new property
ChatResponseExtensions.cs Modifies coalescing logic to respect protected data boundaries
TextReasoningContentTests.cs Adds test coverage for the new property
Microsoft.Extensions.AI.Abstractions.json Registers the new property as stable API

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@stephentoub stephentoub merged commit 3176d4c into dotnet:main Sep 15, 2025
6 checks passed
@stephentoub stephentoub deleted the encryptedreasoning branch September 15, 2025 12:55
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-ai Microsoft.Extensions.AI libraries

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consider adding an EncryptedReasoningBlock

2 participants