objectiveai_sdk/viewer/
events.rs1use schemars::JsonSchema;
13use serde::{Deserialize, Serialize};
14use tokio::sync::mpsc;
15
16use super::ApiCallSubType;
17
18#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
28#[serde(tag = "type", rename_all = "snake_case")]
29#[schemars(rename = "viewer.Event")]
30pub enum Event {
31 #[schemars(title = "Inbound")]
38 Inbound {
39 destination: String,
40 sub_type: String,
41 value: serde_json::Value,
42 },
43 #[schemars(title = "CliCommand")]
49 CliCommand {
50 destination: String,
51 value: serde_json::Value,
52 },
53 #[schemars(title = "ApiCall")]
62 ApiCall {
63 destination: String,
64 sub_type: ApiCallSubType,
65 value: serde_json::Value,
66 },
67}
68
69impl Event {
70 pub fn destination(&self) -> &str {
73 match self {
74 Event::Inbound { destination, .. } => destination,
75 Event::CliCommand { destination, .. } => destination,
76 Event::ApiCall { destination, .. } => destination,
77 }
78 }
79}
80
81pub type EventReceiver = mpsc::UnboundedReceiver<Event>;
82pub type EventSender = mpsc::UnboundedSender<Event>;
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87 use serde_json::json;
88
89 #[test]
90 fn inbound_serializes_with_tag_and_sub_type() {
91 let e = Event::Inbound {
92 destination: "objectiveai".to_string(),
93 sub_type: "agent_completions".to_string(),
94 value: json!({"id": "abc"}),
95 };
96 let s = serde_json::to_string(&e).unwrap();
97 let v: serde_json::Value = serde_json::from_str(&s).unwrap();
98 assert_eq!(v["type"], "inbound");
99 assert_eq!(v["destination"], "objectiveai");
100 assert_eq!(v["sub_type"], "agent_completions");
101 assert_eq!(v["value"], json!({"id": "abc"}));
102
103 let back: Event = serde_json::from_str(&s).unwrap();
104 match back {
105 Event::Inbound { destination, sub_type, value } => {
106 assert_eq!(destination, "objectiveai");
107 assert_eq!(sub_type, "agent_completions");
108 assert_eq!(value, json!({"id": "abc"}));
109 }
110 _ => panic!("expected Inbound"),
111 }
112 }
113
114 #[test]
115 fn cli_command_serializes_with_tag_and_no_sub_type() {
116 let e = Event::CliCommand {
117 destination: "my_plugin".to_string(),
118 value: json!({"type": "notification", "value": {"x": 1}}),
119 };
120 let s = serde_json::to_string(&e).unwrap();
121 let v: serde_json::Value = serde_json::from_str(&s).unwrap();
122 assert_eq!(v["type"], "cli_command");
123 assert_eq!(v["destination"], "my_plugin");
124 assert!(v.get("sub_type").is_none());
125 assert_eq!(v["value"]["type"], "notification");
126 }
127
128 #[test]
129 fn destination_accessor() {
130 let i = Event::Inbound {
131 destination: "d1".to_string(),
132 sub_type: "s".to_string(),
133 value: json!(null),
134 };
135 let c = Event::CliCommand {
136 destination: "d2".to_string(),
137 value: json!(null),
138 };
139 let a = Event::ApiCall {
140 destination: "d3".to_string(),
141 sub_type: ApiCallSubType::PostAgentCompletions,
142 value: json!(null),
143 };
144 assert_eq!(i.destination(), "d1");
145 assert_eq!(c.destination(), "d2");
146 assert_eq!(a.destination(), "d3");
147 }
148
149 #[test]
150 fn api_call_serializes_with_method_underscore_path_subtype() {
151 let e = Event::ApiCall {
152 destination: "my_plugin".to_string(),
153 sub_type: ApiCallSubType::PostAgentCompletions,
154 value: json!({"type": "chunk", "chunk": {"id": "abc"}}),
155 };
156 let s = serde_json::to_string(&e).unwrap();
157 let v: serde_json::Value = serde_json::from_str(&s).unwrap();
158 assert_eq!(v["type"], "api_call");
159 assert_eq!(v["destination"], "my_plugin");
160 assert_eq!(v["sub_type"], "POST_/agent/completions");
161 assert_eq!(v["value"]["type"], "chunk");
162
163 let back: Event = serde_json::from_str(&s).unwrap();
164 match back {
165 Event::ApiCall { destination, sub_type, value } => {
166 assert_eq!(destination, "my_plugin");
167 assert_eq!(sub_type, ApiCallSubType::PostAgentCompletions);
168 assert_eq!(value["chunk"]["id"], "abc");
169 }
170 _ => panic!("expected ApiCall"),
171 }
172 }
173}