Skip to content

fix(rust-sdk): ag-ui-core — round-trip non-UUID Thread/Run/Message/Agent IDs#2196

Open
martinsotirov wants to merge 2 commits into
ag-ui-protocol:mainfrom
infinirealm:fix/non-uuid-ids
Open

fix(rust-sdk): ag-ui-core — round-trip non-UUID Thread/Run/Message/Agent IDs#2196
martinsotirov wants to merge 2 commits into
ag-ui-protocol:mainfrom
infinirealm:fix/non-uuid-ids

Conversation

@martinsotirov

Copy link
Copy Markdown

Fixes #2195.

What

ThreadId/RunId/MessageId/AgentId were strict Uuid newtypes whose FromStr/Deserialize rejected any non-UUID string. The AG-UI protocol (and the TS @ag-ui/core) treat these IDs as plain strings, so any server with non-UUID IDs (LangGraph lc_run--…, composite/namespaced IDs) couldn't round-trip events through the Rust SDK.

This makes the newtypes accept and round-trip arbitrary strings — a UUID-v5 hash backs the as_uuid() view while the original string is preserved for Display/serialize — so valid UUIDs behave exactly as before and non-UUID IDs now interoperate.

Provenance

The fix itself is @MikeSchirtzinger's work from the server-crate branch (#972), commits 90b5205e + 5e50a9f3. It's currently entangled in that large, unmerged PR. This PR extracts just the ids.rs change, rebased onto current main, so it can land independently of the server crate. Full credit to Mike (co-authored).

Changes (minimal + focused)

  • crates/ag-ui-core/src/types/ids.rs — the round-trip implementation + tests.
  • workspace Cargo.toml — enable the uuid v5 feature (needed for new_v5). Nothing else.

Verification

Against current main (edition 2024): cargo build -p ag-ui-core and cargo test -p ag-ui-core both pass, including test_serialize_roundtrip_arbitrary_string, test_run_id_deserialize_non_uuid_string, test_message_id_deserialize_langgraph_format.

Happy to adjust scope or squash as maintainers prefer.

…ent IDs

The ID newtypes were strict Uuid wrappers whose FromStr/Deserialize rejected
any non-UUID string, but the AG-UI protocol treats these IDs as plain strings.
This makes them accept and round-trip arbitrary strings (UUID-v5 hash for the
Uuid view, original string preserved for Display/serialize), so non-UUID IDs
(LangGraph lc_run--…, composite ids) interoperate.

Extracted from Mike Schirtzinger's fix on the ag-ui-protocol#972 server-crate branch
(90b5205, 5e50a9f), rebased onto main as a focused standalone change:
just crates/ag-ui-core/src/types/ids.rs + enabling the uuid `v5` feature.

Fixes ag-ui-protocol#2195.

Co-authored-by: Mike Schirtzinger <155995654+MikeSchirtzinger@users.noreply.github.com>
@martinsotirov
martinsotirov requested a review from a team as a code owner July 15, 2026 12:36
Addresses cross-model review of the initial fix. The extraction keyed
PartialEq/Hash/Serialize on the UUID-v5 backing value, so distinct
strings could collide — and ag-ui-client uses HashSet<&MessageId>.

Make the exact protocol string the identity for PartialEq/Eq/Hash/
Serialize/Display; the UUID becomes a derived compatibility view only
(as_uuid/From<Uuid>/Into<Uuid>/AsRef<Uuid>/PartialEq<Uuid> unchanged).
new() preserves the exact input string for BOTH UUID- and non-UUID-
shaped inputs, so different spellings of the same UUID stay distinct —
matching the plain-string IDs of the AG-UI protocol and TS SDK.

Also updates Cargo.lock for the uuid `v5` feature (sha1_smol), which
the prior commit left stale (--locked builds would otherwise fail).

Tests: identity independent of the backing UUID; UUID-spelling
distinctness; exact-spelling + serde round-trip preservation.
@martinsotirov

Copy link
Copy Markdown
Author

Pushed a follow-up commit (20a6248e): made the protocol string the identity for PartialEq/Hash/Serialize/Display. The initial extraction keyed these on the UUID-v5 backing value, so two distinct strings colliding under v5 would compare equal and collide as HashSet/HashMap keys — and ag-ui-client uses HashSet<&MessageId>. The UUID is now a derived compatibility view only (as_uuid()/From<Uuid>/PartialEq<Uuid> unchanged), and new() preserves the exact input string for both UUID- and non-UUID-shaped inputs, so different spellings of the same UUID stay distinct — matching the plain-string IDs in the TS SDK. Also synced Cargo.lock for the uuid v5 feature (sha1_smol), which the first commit left stale.

cargo test -p ag-ui-core (26 ID tests) and cargo test -p ag-ui-client pass. Happy to squash the two commits if you'd prefer a single one.

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.

Rust ag-ui-core: ThreadId/RunId/MessageId reject non-UUID string IDs

1 participant