Replies: 1 comment 1 reply
|
I work in CopilotKit (the owner of the AG-UI protocol) and I helped refine the metadata spec and it released either yesterday or today. We plan to support it but we just didn't have time to implement it yet |
1 reply
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Question
The AG-UI spec defines metadata merging into messages: every message-building
event carries an optional
metadata, and the consumer folds each event'smetadatainto the message it builds — last-write-wins, key by key(AG-UI › Concepts › Metadata › "Merging into messages").
The spec names the TypeScript client as the reference consumer.
Does
@tanstack/aiintend to implement this? Today it doesn't appear to, andUIMessagehas nometadatafield to receive it. I'd like per-messagemetadatato be preserved onto the resulting
UIMessage- including on user messages,where the metadata arrives inside a
MESSAGES_SNAPSHOTrather than through streamedTEXT_MESSAGE_*events (those are alwaysrole: "assistant"per the spec).Versions
@tanstack/ai: 0.43.0@tanstack/ai-client: 0.23.0Current behavior
UIMessagehas nometadatafield:In
StreamProcessor, eventmetadatais read only for tool calls(
TOOL_CALL_STARTfoldschunk.metadataonto theToolCallPart) andinterrupt descriptors.
The
TEXT_MESSAGE_START/TEXT_MESSAGE_CONTENT/TEXT_MESSAGE_ENDhandlersread only
messageId/role/delta/content. Eventmetadatais neverread, so it never reaches the message.
The text-part updater builds
{ type: 'text', content }only — no metadata slotis populated.
MESSAGES_SNAPSHOTnormalization rebuilds user messages as{ id, role, parts }, dropping any per-messagemetadata.Result: tool-call metadata survives, but message-level metadata is silently
discarded.
Reproduction
User message (via
MESSAGES_SNAPSHOT)A user message carries its own
metadatainside the snapshot:{ "type": "MESSAGES_SNAPSHOT", "messages": [ { "id": "u1", "role": "user", "content": "Show me failed logins", "metadata": {"author": {"id": "user-42", "name": "Dana"}} } ] }Expected (per spec — the message keeps its own
metadata):{ "id": "u1", "role": "user", "parts": [{"type": "text", "content": "Show me failed logins"}], "metadata": {"author": {"id": "user-42", "name": "Dana"}} }Actual: snapshot normalization rebuilds the user message as
{ id, role, parts },so
metadatais dropped.Assistant message (via streamed
TEXT_MESSAGE_*)For completeness, the same loss happens on the streaming path (metadata on the
terminal event, as the spec recommends for end-of-message values):
{"type":"TEXT_MESSAGE_START","messageId":"m1","role":"assistant","metadata":{"source":"openai","stage":"start"}} {"type":"TEXT_MESSAGE_CONTENT","messageId":"m1","delta":"Hello","metadata":{"stage":"content"}} {"type":"TEXT_MESSAGE_END","messageId":"m1","metadata":{"stage":"end","usage":{"output":340},"traceId":"abc-123"}}Expected (per spec):
{ "id": "m1", "role": "assistant", "metadata": { "source": "openai", "stage": "end", "usage": {"output": 340}, "traceId": "abc-123" } }Actual: the assembled
UIMessagehas nometadata; all metadata is lost.Use case (why this matters)
A common need is attaching declared, typed per-message information that is only
known as the message completes — token usage per message, finish reason, trace id,
and message attribution/author in multi-user conversations. The AG-UI metadata
channel is the sanctioned home for this, but because it isn't accumulated onto
UIMessage, there's no supported per-message home for that data.Proposed behavior
metadata?: Record<string, any>toUIMessage.StreamProcessor, merge each message-building event'smetadatainto thetarget message (keyed by
messageId), last-write-wins, key by key —mirroring the existing tool-call metadata handling.
TEXT_MESSAGE_START/_CONTENT/_END→ merge into the message.TOOL_CALL_RESULT→ merge into the tool message it creates.MESSAGES_SNAPSHOT→ preserve each message's ownmetadata(event-levelmetadata not merged).
ag-uikey.UIMessage.metadata.All reactions