ag-ui-protocol / ag-ui-protocol/ag-ui
feat(transport): implement httpBinary (protobuf-over-HTTP) on the Python side (ag_ui encoder + ag-ui-adk)
- 主要言語
- Python
- スター
- 15.9k
- フォーク
- 1.4k
- 平均マージ
- 1日 17時間
- マージ済み PR(30日)
- 163
説明
## Summary
The AG-UI spec reserves an `httpBinary` transport — `TransportCapabilities.httpBinary`: *"the AG-UI binary protocol (protobuf over HTTP)"*, media type `application/vnd.ag-ui.event+proto`. The **TypeScript client already supports it** end-to-end (`@ag-ui/proto` `encode`/`decode`, and `parseProtoStream` in `@ag-ui/client` detects `AGUI_MEDIA_TYPE` and decodes 4-byte length-prefixed protobuf frames).
But the **Python side has no protobuf support at all**: `ag_ui.encoder.EventEncoder` is a no-op stub whose `encode()` always returns an SSE string and `get_content_type()` always returns `text/event-stream`, regardless of the `accept` argument. There is no generated `*_pb2` in the Python SDK. As a result, a Python AG-UI server (including `ag-ui-adk`) can never actually serve the `httpBinary` transport, even though a client requests it.
## Motivation
- **It's the spec's designated binary transport** — the only high-performance/low-bandwidth option AG-UI defines, alongside SSE and WebSocket. Right now it's half-implemented (client yes, Python server no).
- **Bandwidth / throughput**: protobuf frames are markedly smaller than `data: {json}\n\n` SSE, which matters for high-volume event streams (large `STATE_SNAPSHOT`/`MESSAGES_SNAPSHOT`, chunked deltas).
- **Enterprise gateways**: protobuf-over-HTTP/2 passes cleanly through proxies (Envoy, etc.) as ordinary streaming HTTP, no special handling.
## Proposal
Implement `httpBinary` on the Python side, symmetric with `@ag-ui/proto`:
### 1. `sdks/python/ag_ui` — real protobuf codec
- Generate `events_pb2.py`, `types_pb2.py`, `patch_pb2.py` from the existing `.proto` files (the same schemas `@ag-ui/proto` uses).
- `EventEncoder.get_content_type()`: negotiate from the `accept` header — return `application/vnd.ag-ui.event+proto` when the client accepts it, else `text/event-stream`.
- `EventEncoder.encode()`: when in proto mode, serialize the event to the `Event` oneof wrapper (mirroring the TS `encode`: `baseEvent` nesting + the special cases for `MESSAGES_SNAPSHOT` content parts, `RUN_FINISHED` outcome flattening, and `STATE_DELTA` JSON-patch op enum), returning the bare protobuf bytes. SSE behavior is unchanged.
### 2. `integrations/adk-middleware/python` — framing
- `endpoint.py` already branches to `_legacy_stream` for non-SSE content types via `EventEncoder.get_content_type()`. Add the standard **4-byte big-endian length prefix** per event in that branch when the negotiated type is `AGUI_MEDIA_TYPE`, matching what `parseProtoStream` expects on the client (`view.getUint32(0, false)` + `messageLength` bytes).
- Update `capabilities_endpoint` to advertise `transport.httpBinary: true`.
## Wire format (already fixed by the client)
Per the client's `parseProtoStream`:
```
[4-byte BE uint32 length][protobuf-encoded Event message] [4-byte...][...] ...
```
So the server just needs to emit that framing; no client change required for parsing.
## Scope / non-goals
- SSE and WebSocket transports are untouched; `httpBinary` is additive and opt-in (negotiated via `Accept`).
- No new event types or schema changes — the `.proto` schemas already exist under `@ag-ui/proto`.
- Not gRPC — the spec does not define a gRPC transport; this is the `httpBinary` (protobuf-over-HTTP) transport the spec already reserves.
Happy to implement this (Python encoder + `ag-ui-adk` framing + a round-trip test).
コントリビューションガイド
評価
この issue はまだ評価されていません。