1use std::collections::BTreeMap;
4use std::time::{Duration, SystemTime, UNIX_EPOCH};
5
6use serde_json::{Map, Value};
7
8use crate::pipeline::EventObject;
9use crate::wire;
10
11#[derive(Clone, Debug, PartialEq)]
22#[non_exhaustive]
23pub struct Event {
24 pub id: String,
26 pub session_id: Option<String>,
28 pub project_id: String,
30 pub event_type: String,
32 pub timestamp: SystemTime,
34 pub duration: Option<Duration>,
36 pub input_tokens: Option<u32>,
38 pub output_tokens: Option<u32>,
40 pub sdk_language: Option<String>,
42 pub agentcat_version: Option<String>,
44 pub server_name: Option<String>,
46 pub server_version: Option<String>,
48 pub client_name: Option<String>,
50 pub client_version: Option<String>,
52 pub identify_actor_given_id: Option<String>,
54 pub identify_actor_name: Option<String>,
56 pub identify_actor_data: Option<Map<String, Value>>,
58 pub resource_name: Option<String>,
60 pub parameters: Option<Value>,
62 pub response: Option<Value>,
64 pub user_intent: Option<Value>,
66 pub is_error: Option<bool>,
68 pub error: Option<Value>,
70 pub tags: Option<BTreeMap<String, String>>,
72 pub properties: Option<Map<String, Value>>,
74}
75
76impl Default for Event {
77 fn default() -> Self {
89 Self {
90 id: String::new(),
91 session_id: None,
92 project_id: String::new(),
93 event_type: String::new(),
94 timestamp: UNIX_EPOCH,
95 duration: None,
96 input_tokens: None,
97 output_tokens: None,
98 sdk_language: None,
99 agentcat_version: None,
100 server_name: None,
101 server_version: None,
102 client_name: None,
103 client_version: None,
104 identify_actor_given_id: None,
105 identify_actor_name: None,
106 identify_actor_data: None,
107 resource_name: None,
108 parameters: None,
109 response: None,
110 user_intent: None,
111 is_error: None,
112 error: None,
113 tags: None,
114 properties: None,
115 }
116 }
117}
118
119impl Event {
120 pub(crate) fn from_object(event: &EventObject) -> Event {
124 let text = |key: &str| event.get(key).and_then(Value::as_str).map(str::to_owned);
125 let object = |key: &str| event.get(key).and_then(Value::as_object).cloned();
126 let value = |key: &str| event.get(key).cloned();
127 let tokens = |key: &str| {
128 event
129 .get(key)
130 .and_then(Value::as_u64)
131 .and_then(|n| u32::try_from(n).ok())
132 };
133 Event {
134 id: text("id").unwrap_or_default(),
135 session_id: text("sessionId").filter(|id| !id.is_empty()),
136 project_id: text("projectId").unwrap_or_default(),
137 event_type: text("eventType").unwrap_or_default(),
138 timestamp: event
139 .get("timestamp")
140 .and_then(Value::as_str)
141 .and_then(wire::parse_iso_millis)
142 .unwrap_or(UNIX_EPOCH),
143 duration: event
144 .get("duration")
145 .and_then(Value::as_u64)
146 .map(Duration::from_millis),
147 input_tokens: tokens("inputTokens"),
148 output_tokens: tokens("outputTokens"),
149 sdk_language: text("sdkLanguage"),
150 agentcat_version: text("agentcatVersion"),
151 server_name: text("serverName"),
152 server_version: text("serverVersion"),
153 client_name: text("clientName"),
154 client_version: text("clientVersion"),
155 identify_actor_given_id: text("identifyActorGivenId"),
156 identify_actor_name: text("identifyActorName"),
157 identify_actor_data: object("identifyActorData"),
158 resource_name: text("resourceName"),
159 parameters: value("parameters"),
160 response: value("response"),
161 user_intent: value("userIntent"),
162 is_error: event.get("isError").and_then(Value::as_bool),
163 error: value("error"),
164 tags: object("tags").map(|tags| {
165 tags.into_iter()
166 .filter_map(|(key, tag)| Some((key, tag.as_str()?.to_owned())))
167 .collect()
168 }),
169 properties: object("properties"),
170 }
171 }
172
173 pub(crate) fn into_object(self) -> EventObject {
178 let mut out = EventObject::new();
179 let mut put = |key: &str, value: Option<Value>| {
180 if let Some(value) = value {
181 out.insert(key.to_owned(), value);
182 }
183 };
184 let text = |value: Option<String>| value.map(Value::String);
185 put("id", Some(Value::String(self.id)));
186 put(
187 "sessionId",
188 Some(Value::String(self.session_id.unwrap_or_default())),
189 );
190 put("projectId", Some(Value::String(self.project_id)));
191 put("eventType", Some(Value::String(self.event_type)));
192 put(
193 "timestamp",
194 Some(Value::String(wire::iso_millis(self.timestamp))),
195 );
196 put(
197 "duration",
198 self.duration
199 .and_then(|d| u64::try_from(d.as_millis()).ok())
200 .map(Value::from),
201 );
202 put("inputTokens", self.input_tokens.map(Value::from));
203 put("outputTokens", self.output_tokens.map(Value::from));
204 put("sdkLanguage", text(self.sdk_language));
205 put("agentcatVersion", text(self.agentcat_version));
206 put("serverName", text(self.server_name));
207 put("serverVersion", text(self.server_version));
208 put("clientName", text(self.client_name));
209 put("clientVersion", text(self.client_version));
210 put("identifyActorGivenId", text(self.identify_actor_given_id));
211 put("identifyActorName", text(self.identify_actor_name));
212 put(
213 "identifyActorData",
214 self.identify_actor_data.map(Value::Object),
215 );
216 put("resourceName", text(self.resource_name));
217 put("parameters", self.parameters);
218 put("response", self.response);
219 put("userIntent", self.user_intent);
220 put("isError", self.is_error.map(Value::Bool));
221 put("error", self.error);
222 put(
223 "tags",
224 self.tags.map(|tags| {
225 Value::Object(
226 tags.into_iter()
227 .map(|(key, tag)| (key, Value::String(tag)))
228 .collect(),
229 )
230 }),
231 );
232 put("properties", self.properties.map(Value::Object));
233 out
234 }
235}