SIGN IN SIGN UP

fix(langchain): preserve content-block shape in PIIMiddleware redaction (#39894)

`PIIMiddleware` redacted message content via `str(message.content)`.
When `.content` is a list of content blocks rather than a plain string,
`str(...)` produces the `repr` of the list — so a redacted user message
was stored as the literal string:

```
"[{'type': 'text', 'text': 'my email is [REDACTED_EMAIL]'}]"
```

The PII itself was still detected and redacted; only the shape of the
message was destroyed. Anyone sending multimodal or provider-native
block content through an agent with `PIIMiddleware` had their messages
silently flattened into that repr before reaching the model.

`_process_content` now accepts either shape and walks content blocks,
redacting the text of each block in place and leaving non-text blocks
untouched. Plain-string content behaves exactly as before.

While fixing the call sites, this also drops the hand-rebuilding of
messages (`HumanMessage(content=..., id=..., name=...)`) in favor of
`model_copy`. The enumerated rebuild had been quietly discarding every
field it did not list — `additional_kwargs`, `response_metadata`, and so
on — on any message that contained PII.

## Release note

Fixed `PIIMiddleware` flattening list-of-content-blocks message content
into its string `repr` during redaction. Redaction now preserves the
original content shape, and no longer drops message fields such as
`additional_kwargs` on redacted messages.

---

*Written with the help of an AI agent (Claude Code).*
S
Sydney Runkle committed
4a66e355da07b8fad7d6cb8db99287fb4d7e6d83
Parent: 38e2113
Committed by GitHub <noreply@github.com> on 8/27/2026, 4:46:14 PM