Skip to content

fix: sanitize malformed thinking blocks before Anthropic serialization - #69

Open
Michael J. Jabbour (michaeljabbour) wants to merge 1 commit into
microsoft:mainfrom
michaeljabbour:fix/207-sanitize-thinking-blocks
Open

fix: sanitize malformed thinking blocks before Anthropic serialization#69
Michael J. Jabbour (michaeljabbour) wants to merge 1 commit into
microsoft:mainfrom
michaeljabbour:fix/207-sanitize-thinking-blocks

Conversation

@michaeljabbour

Copy link
Copy Markdown

Problem

Cross-provider sessions persist thinking-shaped blocks that the Anthropic API rejects on resume, causing 400 errors and bricked sessions. Two different producers have been observed creating malformed thinking blocks:

  • Chat-completions producer: wraps reasoning_content in ThinkingBlock(signature: null)
  • OpenAI Responses producer: generates thinking blocks with empty thinking field and missing/null signature

When these blocks persist into session history and get replayed to Anthropic on resume, strict validation fails with a 400 that terminates the session (real incidents from both producers confirmed).

Solution

Added _sanitize_thinking_blocks(messages) staticmethod in the shared request-construction path (_complete_chat_request, before _apply_message_cache_control). This method:

  • Strips thinking blocks lacking a valid non-empty string signature
  • Inserts a placeholder message if a message would empty
  • Preserves valid thinking and redacted_thinking blocks unchanged
  • Never raises exceptions (per-message try/except with logger.exception fallback)
  • Emits WARNING log + provider:thinking_blocks_sanitized event
  • Inherited by Fable subclass automatically (runs before cache logic)

Accuracy note: A hypothetical thinking block missing the thinking key entirely is structurally unreachable—amplifier_core.message_models.ThinkingBlock requires thinking by schema, and real producers always set it. The sanitizer targets only the shapes that actually reach the provider in practice.

Testing

  • 19 new test cases covering both malformed shapes, placeholder insertion, mixed content, no-mutation guarantees, and defensive never-raise behavior
  • Test suite: 545 passed, 3 pre-existing unrelated failures (test_tool_repair.py streaming mock issue, present on main)
  • DTU end-to-end: An isolated environment injected a transcript with both malformed shapes and resumed successfully with Stripped 2 thinking block(s)… logged to stderr. Previously a guaranteed 400.

Compatibility

  • No changes to mount(), complete(), or other public contracts
  • Merges cleanly with the effort-clamp PR (fix/289) — disjoint init.py regions, validated in combined DTU test

Fixes microsoft-amplifier/amplifier-support#207

Cross-provider sessions can contain thinking-shaped blocks that the Anthropic API rejects on resume, causing 400 errors and bricked sessions. Added _sanitize_thinking_blocks() in the shared request path (_complete_chat_request, before cache-control application, inherited by fable subclass): strips thinking blocks lacking valid non-empty string signatures, covers both real-world shapes (signature:null from chat-completions; missing signature key from OpenAI Responses producer).

- Inserts placeholder when message content empties (API rejects empty arrays)
- Preserves valid thinking and redacted_thinking fields
- Never raises, logs WARNING on strip, emits provider:thinking_blocks_sanitized event
- 19 new tests covering both signature shapes, placeholder insertion, mixed content, no-mutation defensive suite

Fixes: microsoft-amplifier/amplifier-support#207

Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant