use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use tokio::sync::mpsc;
use super::ApiCallSubType;
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "type", rename_all = "snake_case")]
#[schemars(rename = "viewer.Event")]
pub enum Event {
#[schemars(title = "Inbound")]
Inbound {
destination: String,
sub_type: String,
value: serde_json::Value,
},
#[schemars(title = "CliCommand")]
CliCommand {
destination: String,
value: serde_json::Value,
},
#[schemars(title = "ApiCall")]
ApiCall {
destination: String,
sub_type: ApiCallSubType,
value: serde_json::Value,
},
}
impl Event {
pub fn destination(&self) -> &str {
match self {
Event::Inbound { destination, .. } => destination,
Event::CliCommand { destination, .. } => destination,
Event::ApiCall { destination, .. } => destination,
}
}
}
pub type EventReceiver = mpsc::UnboundedReceiver<Event>;
pub type EventSender = mpsc::UnboundedSender<Event>;
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn inbound_serializes_with_tag_and_sub_type() {
let e = Event::Inbound {
destination: "objectiveai".to_string(),
sub_type: "agent_completions".to_string(),
value: json!({"id": "abc"}),
};
let s = serde_json::to_string(&e).unwrap();
let v: serde_json::Value = serde_json::from_str(&s).unwrap();
assert_eq!(v["type"], "inbound");
assert_eq!(v["destination"], "objectiveai");
assert_eq!(v["sub_type"], "agent_completions");
assert_eq!(v["value"], json!({"id": "abc"}));
let back: Event = serde_json::from_str(&s).unwrap();
match back {
Event::Inbound { destination, sub_type, value } => {
assert_eq!(destination, "objectiveai");
assert_eq!(sub_type, "agent_completions");
assert_eq!(value, json!({"id": "abc"}));
}
_ => panic!("expected Inbound"),
}
}
#[test]
fn cli_command_serializes_with_tag_and_no_sub_type() {
let e = Event::CliCommand {
destination: "my_plugin".to_string(),
value: json!({"type": "notification", "value": {"x": 1}}),
};
let s = serde_json::to_string(&e).unwrap();
let v: serde_json::Value = serde_json::from_str(&s).unwrap();
assert_eq!(v["type"], "cli_command");
assert_eq!(v["destination"], "my_plugin");
assert!(v.get("sub_type").is_none());
assert_eq!(v["value"]["type"], "notification");
}
#[test]
fn destination_accessor() {
let i = Event::Inbound {
destination: "d1".to_string(),
sub_type: "s".to_string(),
value: json!(null),
};
let c = Event::CliCommand {
destination: "d2".to_string(),
value: json!(null),
};
let a = Event::ApiCall {
destination: "d3".to_string(),
sub_type: ApiCallSubType::PostAgentCompletions,
value: json!(null),
};
assert_eq!(i.destination(), "d1");
assert_eq!(c.destination(), "d2");
assert_eq!(a.destination(), "d3");
}
#[test]
fn api_call_serializes_with_method_underscore_path_subtype() {
let e = Event::ApiCall {
destination: "my_plugin".to_string(),
sub_type: ApiCallSubType::PostAgentCompletions,
value: json!({"type": "chunk", "chunk": {"id": "abc"}}),
};
let s = serde_json::to_string(&e).unwrap();
let v: serde_json::Value = serde_json::from_str(&s).unwrap();
assert_eq!(v["type"], "api_call");
assert_eq!(v["destination"], "my_plugin");
assert_eq!(v["sub_type"], "POST_/agent/completions");
assert_eq!(v["value"]["type"], "chunk");
let back: Event = serde_json::from_str(&s).unwrap();
match back {
Event::ApiCall { destination, sub_type, value } => {
assert_eq!(destination, "my_plugin");
assert_eq!(sub_type, ApiCallSubType::PostAgentCompletions);
assert_eq!(value["chunk"]["id"], "abc");
}
_ => panic!("expected ApiCall"),
}
}
}