Skip to main content

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#![cfg_attr(
38    feature = "sse",
39    doc = "Use [`sse`](crate::encode::sse), 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}