ag_ui/encode/protobuf.rs
1//! The AG-UI binary transport — **not implemented**.
2//!
3//! This module exists so that a build with the `protobuf` feature can still
4//! negotiate and name the media type, and so the reason it does nothing is
5//! written down next to the code rather than discovered at runtime.
6//!
7//! # Why there is no encoder here
8//!
9//! The binary transport is defined by `events.proto` in the upstream
10//! `@ag-ui/proto` package. Its `Event` message is a `oneof` over **21** of the
11//! protocol's **36** event types:
12//!
13//! `TEXT_MESSAGE_START`, `TEXT_MESSAGE_CONTENT`, `TEXT_MESSAGE_END`,
14//! `TEXT_MESSAGE_CHUNK`, `TOOL_CALL_START`, `TOOL_CALL_ARGS`, `TOOL_CALL_END`,
15//! `TOOL_CALL_CHUNK`, `STATE_SNAPSHOT`, `STATE_DELTA`, `MESSAGES_SNAPSHOT`,
16//! `RAW`, `CUSTOM`, `RUN_STARTED`, `RUN_FINISHED`, `RUN_ERROR`, `STEP_STARTED`,
17//! `STEP_FINISHED`, `SUBAGENT_STARTED`, `SUBAGENT_FINISHED`, `SUBAGENT_ERROR`.
18//!
19//! (Upstream's own documentation says 19: the two `*_CHUNK` events have a
20//! `oneof` arm but no `EventType` enum value to select it with, so they cannot
21//! be encoded in practice either.)
22//!
23//! The other 15 have no wire representation at all: every `REASONING_*` event,
24//! both `ACTIVITY_*` events, all five deprecated `THINKING_*` events, and
25//! `TOOL_CALL_RESULT`. An agent that reasons, reports activities, or returns a
26//! tool result — which is most of them — cannot express its stream in this
27//! format. Encoding such a run would mean silently dropping events, so this
28//! crate declines to encode any.
29//!
30//! Generating the Rust types would also mean either a `build.rs` that requires
31//! `protoc` on every consumer's machine or checked-in generated code that
32//! drifts from upstream. Neither is worth it for a format that cannot carry the
33//! protocol.
34//!
35//! # What to do instead
36//!
37#, which carries all 36 event types. Revisit"
40)]
41#![cfg_attr(
42 not(feature = "sse"),
43 doc = "Use the `sse` module — enable the `sse` feature — which carries all 36",
44 doc = "event types. Revisit"
45)]
46//! this module when upstream `events.proto` covers the full set.
47
48use crate::encode::{EventStreamFormatter, PROTOBUF_MEDIA_TYPE};
49use crate::error::{Error, Result};
50use crate::event::{Event, EventType};
51
52/// Placeholder for the binary formatter.
53///
54/// [`content_type`](EventStreamFormatter::content_type) reports the negotiated
55/// media type, but [`encode`](EventStreamFormatter::encode) always fails with
56/// [`Error::UnsupportedTransport`] — see the [module docs](self) for why.
57#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
58pub struct ProtobufFormatter;
59
60impl ProtobufFormatter {
61 /// Builds the placeholder formatter.
62 pub const fn new() -> Self {
63 Self
64 }
65}
66
67impl EventStreamFormatter for ProtobufFormatter {
68 fn content_type(&self) -> &'static str {
69 PROTOBUF_MEDIA_TYPE
70 }
71
72 fn encode(&self, _event: &Event) -> Result<Vec<u8>> {
73 Err(Error::UnsupportedTransport(
74 "the AG-UI protobuf schema covers only 21 of 36 event types; use SSE",
75 ))
76 }
77}
78
79/// The event types upstream `events.proto` can represent.
80///
81/// Everything not listed here has no binary encoding. Useful for asserting in a
82/// test that a given stream would survive the binary transport.
83pub const COVERED_EVENT_TYPES: &[EventType] = &[
84 EventType::TextMessageStart,
85 EventType::TextMessageContent,
86 EventType::TextMessageEnd,
87 EventType::TextMessageChunk,
88 EventType::ToolCallStart,
89 EventType::ToolCallArgs,
90 EventType::ToolCallEnd,
91 EventType::ToolCallChunk,
92 EventType::StateSnapshot,
93 EventType::StateDelta,
94 EventType::MessagesSnapshot,
95 EventType::Raw,
96 EventType::Custom,
97 EventType::RunStarted,
98 EventType::RunFinished,
99 EventType::RunError,
100 EventType::StepStarted,
101 EventType::StepFinished,
102 EventType::SubagentStarted,
103 EventType::SubagentFinished,
104 EventType::SubagentError,
105];
106
107/// Whether the binary transport can represent `event_type`.
108pub fn is_covered(event_type: EventType) -> bool {
109 COVERED_EVENT_TYPES.contains(&event_type)
110}