SIGN IN SIGN UP

fix(agui): key tool and content dedup by ordinal (#14759)

* fix(agui): key tool and content dedup by ordinal

AG-UI dedup derived a tool call's id from its absolute position in
content_blocks, assuming that list is append-only. Message.text's setter
breaks the assumption: it drops every TextContent block and appends one
consolidated block at the end, so any block that had narration text
ahead of it shifts index. ChatOutput.message_response() triggers exactly
that on an Agent message that already finished streaming, then re-emits
it, and the shifted blocks read as brand new - re-firing a full
TOOL_CALL_START/ARGS/END/RESULT quartet under a fresh id, with the same
name, input and output, and ~zero duration.

Key off content["id"] when a producer stamped one, else off the leaf's
rank among the leaves of its own kind in the traversal. Only TextContent
blocks move, so that rank survives the reindexing. Ranking also keeps
two identical tool calls distinct, which a content fingerprint would
have collapsed.

Custom content blocks (json/code/media/error) were keyed the same way
and duplicate for the same reason, so they get the same treatment.

Fixes LE-2297
Fixes #14668

Co-authored-by: YasserManss <Yaser775358@gmail.com>

* fix(agui): separate id and ordinal key namespaces

A message can mix producer-stamped and unstamped leaves, and both keys
were minted into one namespace. [tool(id="1"), tool(id=None)] mapped
both onto m:tool:1, so the second call's whole lifecycle was dropped;
the same collision on custom content made two blocks overwrite each
other's fingerprint and re-emit on every re-fire. Mint stamped ids
under :id: and ordinals under :ord: so they cannot meet.

Also covers grouped leaves, whose nested tool_use / custom content the
traversal ordinal already handled but no test exercised.

---------

Co-authored-by: YasserManss <Yaser775358@gmail.com>
Co-authored-by: Eric Hare <ericrhare@gmail.com>
C
Cristhian Zanforlin Lousa committed
2163236b815d0d0cdce8ce92358ba2b78df838b7
Parent: 43083e6
Committed by GitHub <noreply@github.com> on 8/25/2026, 7:34:07 PM