ag-ui-protocol / ag-ui-protocol/ag-ui
[Feature]: Add AGUIStreamOptions.IncludeRawEvents to make rawEvent attachment opt-out (.NET)
- Langage dominant
- Python
- Étoiles
- 15.9k
- Forks
- 1.4k
- Merge moyen
- 1 j 17 h
- PR mergées (30 j)
- 163
Description
### Pre-flight Checklist
- [x] I have searched existing issues and this hasn't been requested yet.
### Problem or Motivation
`AsAGUIEventStreamAsync` serializes every `ChatResponseUpdate` and attaches it as `rawEvent` to each event derived from it (`TEXT_MESSAGE_*`, `TOOL_CALL_*`, `REASONING_ENCRYPTED_VALUE`). There is no way to turn it off.
Measured on this repo's own `Step*` server baselines: `rawEvent` is **66% of the wire** (425,009 → 145,716 bytes when stripped; `TEXT_MESSAGE_CONTENT` +213%, `TOOL_CALL_END` +648%). It rides protobuf as well as SSE.
Nothing reads it. The .NET client's only `RawEvent` reference is the *event type* (`EventStreamConverter.cs:341`); the TS client only writes the field on its own error events (`transform/http.ts:72`).
It is also a .NET-only divergence: no TS or Python server populates `rawEvent` (Python declares `raw_event: Optional[Any] = None` and never sets it), and `agui-cross-sdk-parity/SKILL.md:27` makes TypeScript canonical for wire format.
### Proposed Solution
An init-only `bool IncludeRawEvents` on `AGUIStreamOptions`, default `true` so nothing changes. Guard the serialization, not the assignment, so opting out also recovers the per-update CPU and allocations (`ChatResponseUpdateAGUIExtensions.cs:200`):
```csharp
JsonElement? raw = options.IncludeRawEvents
? JsonSerializer.SerializeToElement(chatResponse, jsonSerializerOptions.GetTypeInfo(typeof(ChatResponseUpdate)))
: null;
```
Every `Create` already takes `JsonElement?`, so this widens one internal signature (`ReasoningMessageTracker.EmitEncryptedValue`). Events the caller supplies — via `RawRepresentation` or a `Map*` callback — keep their own `RawEvent`. Event sequence is unchanged, so this is not a protocol change.
### Alternatives Considered
**A host-side stream filter** that nulls `RawEvent` before the formatter. Works (verified, −61% on a short stream) but still pays the serialization, must be reimplemented per host, and can't tell an SDK-attached payload from a deliberate one.
**Defaulting to `false`.** Arguably correct long-term — it is what puts .NET in parity — but flipping it breaks anyone reading the field and moves every baseline. Better as a separate, deliberate change.
**Emitting `BaseEvent`s via `RawRepresentation`** to hit the short-circuit above line 200. Avoids the serialization entirely, but means reimplementing text/tool/reasoning tracking.
### Additional Context
_No response_
Guide de contribution
Ouvrir le guide de contribution
Évaluation
Cette issue n'a pas encore été évaluée.