1use base64::Engine;
4use base64::engine::general_purpose::STANDARD as BASE64;
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Deserializer, Serialize, Serializer};
7use serde_json::Value;
8use std::collections::HashMap;
9use uuid::Uuid;
10
11pub type TaskId = String;
17
18pub type ArtifactId = String;
20
21pub fn new_task_id() -> TaskId {
23 Uuid::now_v7().to_string()
24}
25
26pub fn new_context_id() -> String {
28 Uuid::now_v7().to_string()
29}
30
31pub fn new_message_id() -> String {
33 Uuid::now_v7().to_string()
34}
35
36pub fn new_artifact_id() -> ArtifactId {
38 Uuid::now_v7().to_string()
39}
40
41#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
47pub enum Role {
48 #[default]
49 Unspecified,
50 User,
51 Agent,
52}
53
54impl Serialize for Role {
55 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
56 match self {
57 Role::Unspecified => serializer.serialize_str("ROLE_UNSPECIFIED"),
58 Role::User => serializer.serialize_str("ROLE_USER"),
59 Role::Agent => serializer.serialize_str("ROLE_AGENT"),
60 }
61 }
62}
63
64impl<'de> Deserialize<'de> for Role {
65 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
66 let s = String::deserialize(deserializer)?;
67 match s.as_str() {
68 "ROLE_USER" => Ok(Role::User),
69 "ROLE_AGENT" => Ok(Role::Agent),
70 "ROLE_UNSPECIFIED" | "" => Ok(Role::Unspecified),
71 other => Err(serde::de::Error::unknown_variant(
72 other,
73 &["ROLE_USER", "ROLE_AGENT", "ROLE_UNSPECIFIED"],
74 )),
75 }
76 }
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
85pub enum TaskState {
86 #[default]
87 Unspecified,
88 Submitted,
89 Working,
90 Completed,
91 Failed,
92 Canceled,
93 InputRequired,
94 Rejected,
95 AuthRequired,
96}
97
98impl TaskState {
99 pub fn is_terminal(&self) -> bool {
101 matches!(
102 self,
103 TaskState::Completed | TaskState::Failed | TaskState::Canceled | TaskState::Rejected
104 )
105 }
106}
107
108impl Serialize for TaskState {
109 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
110 let s = match self {
111 TaskState::Unspecified => "TASK_STATE_UNSPECIFIED",
112 TaskState::Submitted => "TASK_STATE_SUBMITTED",
113 TaskState::Working => "TASK_STATE_WORKING",
114 TaskState::Completed => "TASK_STATE_COMPLETED",
115 TaskState::Failed => "TASK_STATE_FAILED",
116 TaskState::Canceled => "TASK_STATE_CANCELED",
117 TaskState::InputRequired => "TASK_STATE_INPUT_REQUIRED",
118 TaskState::Rejected => "TASK_STATE_REJECTED",
119 TaskState::AuthRequired => "TASK_STATE_AUTH_REQUIRED",
120 };
121 serializer.serialize_str(s)
122 }
123}
124
125impl<'de> Deserialize<'de> for TaskState {
126 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
127 let s = String::deserialize(deserializer)?;
128 match s.as_str() {
129 "TASK_STATE_SUBMITTED" => Ok(TaskState::Submitted),
130 "TASK_STATE_WORKING" => Ok(TaskState::Working),
131 "TASK_STATE_COMPLETED" => Ok(TaskState::Completed),
132 "TASK_STATE_FAILED" => Ok(TaskState::Failed),
133 "TASK_STATE_CANCELED" => Ok(TaskState::Canceled),
134 "TASK_STATE_INPUT_REQUIRED" => Ok(TaskState::InputRequired),
135 "TASK_STATE_REJECTED" => Ok(TaskState::Rejected),
136 "TASK_STATE_AUTH_REQUIRED" => Ok(TaskState::AuthRequired),
137 "TASK_STATE_UNSPECIFIED" | "" => Ok(TaskState::Unspecified),
138 other => Err(serde::de::Error::unknown_variant(
139 other,
140 &[
141 "TASK_STATE_SUBMITTED",
142 "TASK_STATE_WORKING",
143 "TASK_STATE_COMPLETED",
144 "TASK_STATE_FAILED",
145 "TASK_STATE_CANCELED",
146 "TASK_STATE_INPUT_REQUIRED",
147 "TASK_STATE_REJECTED",
148 "TASK_STATE_AUTH_REQUIRED",
149 "TASK_STATE_UNSPECIFIED",
150 ],
151 )),
152 }
153 }
154}
155
156#[derive(Debug, Clone, PartialEq)]
163pub struct Part {
164 pub content: PartContent,
165 #[doc = "Optional filename for file parts."]
166 pub filename: Option<String>,
167 #[doc = "MIME type of the part content."]
168 pub media_type: Option<String>,
169 #[doc = "Optional metadata for extensions."]
170 pub metadata: Option<HashMap<String, Value>>,
171}
172
173#[derive(Debug, Clone, PartialEq)]
175pub enum PartContent {
176 Text(String),
177 Raw(Vec<u8>),
178 Url(String),
179 Data(Value),
180}
181
182impl Part {
183 pub fn text(text: impl Into<String>) -> Self {
184 Part {
185 content: PartContent::Text(text.into()),
186 filename: None,
187 media_type: None,
188 metadata: None,
189 }
190 }
191
192 pub fn raw(data: Vec<u8>) -> Self {
193 Part {
194 content: PartContent::Raw(data),
195 filename: None,
196 media_type: None,
197 metadata: None,
198 }
199 }
200
201 pub fn url(url: impl Into<String>) -> Self {
202 Part {
203 content: PartContent::Url(url.into()),
204 filename: None,
205 media_type: None,
206 metadata: None,
207 }
208 }
209
210 pub fn data(value: Value) -> Self {
211 Part {
212 content: PartContent::Data(value),
213 filename: None,
214 media_type: None,
215 metadata: None,
216 }
217 }
218
219 pub fn with_media_type(mut self, media_type: impl Into<String>) -> Self {
220 self.media_type = Some(media_type.into());
221 self
222 }
223
224 pub fn with_filename(mut self, filename: impl Into<String>) -> Self {
225 self.filename = Some(filename.into());
226 self
227 }
228
229 pub fn as_text(&self) -> Option<&str> {
230 if let PartContent::Text(ref s) = self.content {
231 Some(s)
232 } else {
233 None
234 }
235 }
236}
237
238impl Serialize for Part {
239 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
240 use serde::ser::SerializeMap;
241 let mut map = serializer.serialize_map(None)?;
242
243 match &self.content {
244 PartContent::Text(t) => map.serialize_entry("text", t)?,
245 PartContent::Raw(r) => map.serialize_entry("raw", &BASE64.encode(r))?,
246 PartContent::Url(u) => map.serialize_entry("url", u)?,
247 PartContent::Data(d) => map.serialize_entry("data", d)?,
248 }
249
250 if let Some(ref f) = self.filename {
251 map.serialize_entry("filename", f)?;
252 }
253 if let Some(ref m) = self.media_type {
254 map.serialize_entry("mediaType", m)?;
255 }
256 if let Some(ref meta) = self.metadata {
257 if !meta.is_empty() {
258 map.serialize_entry("metadata", meta)?;
259 }
260 }
261 map.end()
262 }
263}
264
265impl<'de> Deserialize<'de> for Part {
266 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
267 let raw: HashMap<String, Value> = HashMap::deserialize(deserializer)?;
268
269 let content = if let Some(Value::String(t)) = raw.get("text") {
270 PartContent::Text(t.clone())
271 } else if let Some(Value::String(r)) = raw.get("raw") {
272 let bytes = BASE64.decode(r).map_err(serde::de::Error::custom)?;
273 PartContent::Raw(bytes)
274 } else if let Some(Value::String(u)) = raw.get("url") {
275 PartContent::Url(u.clone())
276 } else if let Some(d) = raw.get("data") {
277 PartContent::Data(d.clone())
278 } else {
279 return Err(serde::de::Error::custom(
280 "Part must have one of: text, raw, url, data",
281 ));
282 };
283
284 let filename = raw
285 .get("filename")
286 .and_then(|v| v.as_str())
287 .map(String::from);
288 let media_type = raw
289 .get("mediaType")
290 .and_then(|v| v.as_str())
291 .map(String::from);
292 let metadata: Option<HashMap<String, Value>> = raw
293 .get("metadata")
294 .and_then(|v| serde_json::from_value(v.clone()).ok());
295
296 Ok(Part {
297 content,
298 filename,
299 media_type,
300 metadata,
301 })
302 }
303}
304
305#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
311#[serde(rename_all = "camelCase")]
312pub struct Message {
313 pub message_id: String,
315
316 #[serde(default, skip_serializing_if = "Option::is_none")]
318 pub context_id: Option<String>,
319
320 #[serde(default, skip_serializing_if = "Option::is_none")]
322 pub task_id: Option<TaskId>,
323
324 pub role: Role,
326
327 pub parts: Vec<Part>,
329
330 #[serde(default, skip_serializing_if = "Option::is_none")]
332 pub metadata: Option<HashMap<String, Value>>,
333
334 #[serde(default, skip_serializing_if = "Option::is_none")]
336 pub extensions: Option<Vec<String>>,
337
338 #[serde(default, skip_serializing_if = "Option::is_none")]
340 pub reference_task_ids: Option<Vec<TaskId>>,
341}
342
343impl Message {
344 pub fn new(role: Role, parts: Vec<Part>) -> Self {
346 Message {
347 message_id: new_message_id(),
348 context_id: None,
349 task_id: None,
350 role,
351 parts,
352 metadata: None,
353 extensions: None,
354 reference_task_ids: None,
355 }
356 }
357
358 pub fn text(&self) -> Option<&str> {
360 self.parts.iter().find_map(|p| p.as_text())
361 }
362}
363
364#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
370#[serde(rename_all = "camelCase")]
371pub struct TaskStatus {
372 pub state: TaskState,
373
374 #[serde(default, skip_serializing_if = "Option::is_none")]
375 pub message: Option<Message>,
376
377 #[serde(default, skip_serializing_if = "Option::is_none")]
378 pub timestamp: Option<DateTime<Utc>>,
379}
380
381#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
387#[serde(rename_all = "camelCase")]
388pub struct Task {
389 pub id: TaskId,
390 pub context_id: String,
391 pub status: TaskStatus,
392
393 #[serde(default, skip_serializing_if = "Option::is_none")]
394 pub artifacts: Option<Vec<Artifact>>,
395
396 #[serde(default, skip_serializing_if = "Option::is_none")]
397 pub history: Option<Vec<Message>>,
398
399 #[serde(default, skip_serializing_if = "Option::is_none")]
400 pub metadata: Option<HashMap<String, Value>>,
401}
402
403#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
409#[serde(rename_all = "camelCase")]
410pub struct Artifact {
411 pub artifact_id: ArtifactId,
412
413 #[serde(default, skip_serializing_if = "Option::is_none")]
414 pub name: Option<String>,
415
416 #[serde(default, skip_serializing_if = "Option::is_none")]
417 pub description: Option<String>,
418
419 pub parts: Vec<Part>,
420
421 #[serde(default, skip_serializing_if = "Option::is_none")]
422 pub metadata: Option<HashMap<String, Value>>,
423
424 #[serde(default, skip_serializing_if = "Option::is_none")]
425 pub extensions: Option<Vec<String>>,
426}
427
428#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
434#[serde(rename_all = "camelCase")]
435pub struct SendMessageConfiguration {
436 #[serde(default, skip_serializing_if = "Option::is_none")]
437 pub accepted_output_modes: Option<Vec<String>>,
438
439 #[serde(default, skip_serializing_if = "Option::is_none")]
440 #[serde(rename = "pushNotificationConfig")]
441 pub push_notification_config: Option<PushNotificationConfig>,
442
443 #[serde(default, skip_serializing_if = "Option::is_none")]
444 pub history_length: Option<i32>,
445
446 #[serde(default, skip_serializing_if = "Option::is_none")]
447 pub return_immediately: Option<bool>,
448}
449
450#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
452#[serde(rename_all = "camelCase")]
453pub struct SendMessageRequest {
454 pub message: Message,
455
456 #[serde(default, skip_serializing_if = "Option::is_none")]
457 pub configuration: Option<SendMessageConfiguration>,
458
459 #[serde(default, skip_serializing_if = "Option::is_none")]
460 pub metadata: Option<HashMap<String, Value>>,
461
462 #[serde(default, skip_serializing_if = "Option::is_none")]
463 pub tenant: Option<String>,
464}
465
466#[derive(Debug, Clone, PartialEq)]
468pub enum SendMessageResponse {
469 Task(Task),
470 Message(Message),
471}
472
473impl Serialize for SendMessageResponse {
474 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
475 use serde::ser::SerializeMap;
476 let mut map = serializer.serialize_map(Some(1))?;
477 match self {
478 SendMessageResponse::Task(t) => map.serialize_entry("task", t)?,
479 SendMessageResponse::Message(m) => map.serialize_entry("message", m)?,
480 }
481 map.end()
482 }
483}
484
485impl<'de> Deserialize<'de> for SendMessageResponse {
486 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
487 let raw: HashMap<String, Value> = HashMap::deserialize(deserializer)?;
488 if let Some(v) = raw.get("task") {
489 let task: Task = serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?;
490 Ok(SendMessageResponse::Task(task))
491 } else if let Some(v) = raw.get("message") {
492 let msg: Message =
493 serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?;
494 Ok(SendMessageResponse::Message(msg))
495 } else {
496 Err(serde::de::Error::custom(
497 "SendMessageResponse must have 'task' or 'message'",
498 ))
499 }
500 }
501}
502
503#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
505#[serde(rename_all = "camelCase")]
506pub struct GetTaskRequest {
507 pub id: TaskId,
508
509 #[serde(default, skip_serializing_if = "Option::is_none")]
510 pub history_length: Option<i32>,
511
512 #[serde(default, skip_serializing_if = "Option::is_none")]
513 pub tenant: Option<String>,
514}
515
516#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
518#[serde(rename_all = "camelCase")]
519pub struct ListTasksRequest {
520 #[serde(default, skip_serializing_if = "Option::is_none")]
521 pub context_id: Option<String>,
522
523 #[serde(default, skip_serializing_if = "Option::is_none")]
524 pub status: Option<TaskState>,
525
526 #[serde(default, skip_serializing_if = "Option::is_none")]
527 pub page_size: Option<i32>,
528
529 #[serde(default, skip_serializing_if = "Option::is_none")]
530 pub page_token: Option<String>,
531
532 #[serde(default, skip_serializing_if = "Option::is_none")]
533 pub history_length: Option<i32>,
534
535 #[serde(default, skip_serializing_if = "Option::is_none")]
536 pub status_timestamp_after: Option<DateTime<Utc>>,
537
538 #[serde(default, skip_serializing_if = "Option::is_none")]
539 pub include_artifacts: Option<bool>,
540
541 #[serde(default, skip_serializing_if = "Option::is_none")]
542 pub tenant: Option<String>,
543}
544
545#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
547#[serde(rename_all = "camelCase")]
548pub struct ListTasksResponse {
549 pub tasks: Vec<Task>,
550 pub next_page_token: String,
551 pub page_size: i32,
552 pub total_size: i32,
553}
554
555#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
557#[serde(rename_all = "camelCase")]
558pub struct CancelTaskRequest {
559 pub id: TaskId,
560
561 #[serde(default, skip_serializing_if = "Option::is_none")]
562 pub metadata: Option<HashMap<String, Value>>,
563
564 #[serde(default, skip_serializing_if = "Option::is_none")]
565 pub tenant: Option<String>,
566}
567
568#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
570#[serde(rename_all = "camelCase")]
571pub struct SubscribeToTaskRequest {
572 pub id: TaskId,
573
574 #[serde(default, skip_serializing_if = "Option::is_none")]
575 pub tenant: Option<String>,
576}
577
578#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
580#[serde(rename_all = "camelCase")]
581pub struct GetExtendedAgentCardRequest {
582 #[serde(default, skip_serializing_if = "Option::is_none")]
583 pub tenant: Option<String>,
584}
585
586#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
592#[serde(rename_all = "camelCase")]
593pub struct PushNotificationConfig {
594 pub url: String,
596
597 #[serde(default, skip_serializing_if = "Option::is_none")]
599 pub id: Option<String>,
600
601 #[serde(default, skip_serializing_if = "Option::is_none")]
603 pub token: Option<String>,
604
605 #[serde(default, skip_serializing_if = "Option::is_none")]
607 pub authentication: Option<AuthenticationInfo>,
608}
609
610#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
612#[serde(rename_all = "camelCase")]
613pub struct AuthenticationInfo {
614 pub scheme: String,
616
617 #[serde(default, skip_serializing_if = "Option::is_none")]
619 pub credentials: Option<String>,
620}
621
622#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
624#[serde(rename_all = "camelCase")]
625pub struct TaskPushNotificationConfig {
626 pub task_id: TaskId,
627 pub config: PushNotificationConfig,
628
629 #[serde(default, skip_serializing_if = "Option::is_none")]
630 pub tenant: Option<String>,
631}
632
633#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
635#[serde(rename_all = "camelCase")]
636pub struct GetTaskPushNotificationConfigRequest {
637 pub task_id: TaskId,
638 pub id: String,
639
640 #[serde(default, skip_serializing_if = "Option::is_none")]
641 pub tenant: Option<String>,
642}
643
644#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
646#[serde(rename_all = "camelCase")]
647pub struct ListTaskPushNotificationConfigsRequest {
648 pub task_id: TaskId,
649
650 #[serde(default, skip_serializing_if = "Option::is_none")]
651 pub page_size: Option<i32>,
652
653 #[serde(default, skip_serializing_if = "Option::is_none")]
654 pub page_token: Option<String>,
655
656 #[serde(default, skip_serializing_if = "Option::is_none")]
657 pub tenant: Option<String>,
658}
659
660#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
662#[serde(rename_all = "camelCase")]
663pub struct ListTaskPushNotificationConfigsResponse {
664 pub configs: Vec<TaskPushNotificationConfig>,
665
666 #[serde(default, skip_serializing_if = "Option::is_none")]
667 pub next_page_token: Option<String>,
668}
669
670#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
672#[serde(rename_all = "camelCase")]
673pub struct CreateTaskPushNotificationConfigRequest {
674 pub task_id: TaskId,
675 pub config: PushNotificationConfig,
676
677 #[serde(default, skip_serializing_if = "Option::is_none")]
678 pub tenant: Option<String>,
679}
680
681#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
683#[serde(rename_all = "camelCase")]
684pub struct DeleteTaskPushNotificationConfigRequest {
685 pub task_id: TaskId,
686 pub id: String,
687
688 #[serde(default, skip_serializing_if = "Option::is_none")]
689 pub tenant: Option<String>,
690}
691
692pub type TransportProtocol = String;
698
699pub const TRANSPORT_PROTOCOL_JSONRPC: &str = "JSONRPC";
701pub const TRANSPORT_PROTOCOL_GRPC: &str = "GRPC";
703pub const TRANSPORT_PROTOCOL_HTTP_JSON: &str = "HTTP+JSON";
705pub const TRANSPORT_PROTOCOL_SLIMRPC: &str = "SLIMRPC";
707
708pub type ProtocolVersion = String;
710
711#[cfg(test)]
712mod tests {
713 use super::*;
714
715 #[test]
716 fn test_task_state_serde() {
717 let state = TaskState::Completed;
718 let json = serde_json::to_string(&state).unwrap();
719 assert_eq!(json, r#""TASK_STATE_COMPLETED""#);
720 let back: TaskState = serde_json::from_str(&json).unwrap();
721 assert_eq!(back, TaskState::Completed);
722 }
723
724 #[test]
725 fn test_role_serde() {
726 let role = Role::Agent;
727 let json = serde_json::to_string(&role).unwrap();
728 assert_eq!(json, r#""ROLE_AGENT""#);
729 let back: Role = serde_json::from_str(&json).unwrap();
730 assert_eq!(back, Role::Agent);
731 }
732
733 #[test]
734 fn test_part_text_serde() {
735 let part = Part::text("hello");
736 let json = serde_json::to_string(&part).unwrap();
737 let v: Value = serde_json::from_str(&json).unwrap();
738 assert_eq!(v["text"], "hello");
739 assert!(v.get("raw").is_none());
740
741 let back: Part = serde_json::from_str(&json).unwrap();
742 assert_eq!(back.content, PartContent::Text("hello".into()));
743 }
744
745 #[test]
746 fn test_part_raw_serde() {
747 let part = Part::raw(vec![1, 2, 3]);
748 let json = serde_json::to_string(&part).unwrap();
749 let back: Part = serde_json::from_str(&json).unwrap();
750 assert_eq!(back.content, PartContent::Raw(vec![1, 2, 3]));
751 }
752
753 #[test]
754 fn test_send_message_response_serde() {
755 let resp = SendMessageResponse::Task(Task {
756 id: "t1".into(),
757 context_id: "c1".into(),
758 status: TaskStatus {
759 state: TaskState::Submitted,
760 message: None,
761 timestamp: None,
762 },
763 artifacts: None,
764 history: None,
765 metadata: None,
766 });
767 let json = serde_json::to_string(&resp).unwrap();
768 let v: Value = serde_json::from_str(&json).unwrap();
769 assert!(v.get("task").is_some());
770 assert!(v.get("message").is_none());
771 let back: SendMessageResponse = serde_json::from_str(&json).unwrap();
772 assert!(matches!(back, SendMessageResponse::Task(_)));
773 }
774
775 #[test]
776 fn test_send_message_response_message_variant() {
777 let resp = SendMessageResponse::Message(Message::new(Role::Agent, vec![Part::text("hi")]));
778 let json = serde_json::to_string(&resp).unwrap();
779 let v: Value = serde_json::from_str(&json).unwrap();
780 assert!(v.get("message").is_some());
781 assert!(v.get("task").is_none());
782 let back: SendMessageResponse = serde_json::from_str(&json).unwrap();
783 assert!(matches!(back, SendMessageResponse::Message(_)));
784 }
785
786 #[test]
787 fn test_part_url_serde() {
788 let part = Part::url("https://example.com/file.pdf");
789 let json = serde_json::to_string(&part).unwrap();
790 let v: Value = serde_json::from_str(&json).unwrap();
791 assert_eq!(v["url"], "https://example.com/file.pdf");
792 let back: Part = serde_json::from_str(&json).unwrap();
793 assert!(matches!(back.content, PartContent::Url(_)));
794 }
795
796 #[test]
797 fn test_part_data_serde() {
798 let data = serde_json::json!({"key": "value", "count": 42});
799 let part = Part::data(data.clone());
800 let json = serde_json::to_string(&part).unwrap();
801 let back: Part = serde_json::from_str(&json).unwrap();
802 assert!(matches!(back.content, PartContent::Data(_)));
803 if let PartContent::Data(d) = back.content {
804 assert_eq!(d["key"], "value");
805 }
806 }
807
808 #[test]
809 fn test_part_with_metadata_and_media_type() {
810 let part = Part::text("hello")
811 .with_media_type("text/plain")
812 .with_filename("test.txt");
813 assert_eq!(part.media_type.as_deref(), Some("text/plain"));
814 assert_eq!(part.filename.as_deref(), Some("test.txt"));
815 let json = serde_json::to_string(&part).unwrap();
816 let v: Value = serde_json::from_str(&json).unwrap();
817 assert_eq!(v["mediaType"], "text/plain");
818 assert_eq!(v["filename"], "test.txt");
819 }
820
821 #[test]
822 fn test_part_as_text() {
823 let part = Part::text("hello");
824 assert_eq!(part.as_text(), Some("hello"));
825 let part = Part::raw(vec![]);
826 assert_eq!(part.as_text(), None);
827 }
828
829 #[test]
830 fn test_message_new() {
831 let msg = Message::new(Role::User, vec![Part::text("hi")]);
832 assert!(!msg.message_id.is_empty());
833 assert_eq!(msg.role, Role::User);
834 assert_eq!(msg.parts.len(), 1);
835 assert_eq!(msg.text(), Some("hi"));
836 }
837
838 #[test]
839 fn test_message_serde() {
840 let msg = Message {
841 message_id: "m1".to_string(),
842 context_id: Some("c1".to_string()),
843 task_id: None,
844 role: Role::Agent,
845 parts: vec![Part::text("response")],
846 metadata: None,
847 extensions: Some(vec!["ext1".to_string()]),
848 reference_task_ids: Some(vec!["t1".to_string()]),
849 };
850 let json = serde_json::to_string(&msg).unwrap();
851 let back: Message = serde_json::from_str(&json).unwrap();
852 assert_eq!(msg, back);
853 }
854
855 #[test]
856 fn test_task_state_is_terminal() {
857 assert!(TaskState::Completed.is_terminal());
858 assert!(TaskState::Failed.is_terminal());
859 assert!(TaskState::Canceled.is_terminal());
860 assert!(TaskState::Rejected.is_terminal());
861 assert!(!TaskState::Submitted.is_terminal());
862 assert!(!TaskState::Working.is_terminal());
863 assert!(!TaskState::InputRequired.is_terminal());
864 assert!(!TaskState::AuthRequired.is_terminal());
865 assert!(!TaskState::Unspecified.is_terminal());
866 }
867
868 #[test]
869 fn test_task_full_serde() {
870 let task = Task {
871 id: "t1".to_string(),
872 context_id: "c1".to_string(),
873 status: TaskStatus {
874 state: TaskState::Working,
875 message: Some(Message::new(Role::Agent, vec![Part::text("working")])),
876 timestamp: None,
877 },
878 artifacts: Some(vec![Artifact {
879 artifact_id: "a1".to_string(),
880 name: Some("output".to_string()),
881 description: None,
882 parts: vec![Part::text("result data")],
883 metadata: None,
884 extensions: None,
885 }]),
886 history: Some(vec![Message::new(Role::User, vec![Part::text("do it")])]),
887 metadata: None,
888 };
889 let json = serde_json::to_string(&task).unwrap();
890 let back: Task = serde_json::from_str(&json).unwrap();
891 assert_eq!(task.id, back.id);
892 assert_eq!(task.status.state, back.status.state);
893 assert!(back.artifacts.is_some());
894 assert!(back.history.is_some());
895 }
896
897 #[test]
898 fn test_push_notification_config_serde() {
899 let config = PushNotificationConfig {
900 url: "https://example.com/webhook".to_string(),
901 id: Some("cfg-1".to_string()),
902 token: Some("tok-1".to_string()),
903 authentication: Some(AuthenticationInfo {
904 scheme: "Bearer".to_string(),
905 credentials: Some("secret".to_string()),
906 }),
907 };
908 let json = serde_json::to_string(&config).unwrap();
909 let back: PushNotificationConfig = serde_json::from_str(&json).unwrap();
910 assert_eq!(config, back);
911 }
912
913 #[test]
914 fn test_send_message_request_serde() {
915 let req = SendMessageRequest {
916 message: Message::new(Role::User, vec![Part::text("hello")]),
917 configuration: Some(SendMessageConfiguration {
918 accepted_output_modes: Some(vec!["text/plain".to_string()]),
919 push_notification_config: None,
920 history_length: Some(10),
921 return_immediately: Some(true),
922 }),
923 metadata: None,
924 tenant: Some("tenant-1".to_string()),
925 };
926 let json = serde_json::to_string(&req).unwrap();
927 let back: SendMessageRequest = serde_json::from_str(&json).unwrap();
928 assert_eq!(req.tenant, back.tenant);
929 assert_eq!(
930 req.configuration.as_ref().unwrap().history_length,
931 back.configuration.as_ref().unwrap().history_length
932 );
933 }
934
935 #[test]
936 fn test_role_all_variants_serde() {
937 let cases = [
938 (Role::Unspecified, "\"ROLE_UNSPECIFIED\""),
939 (Role::User, "\"ROLE_USER\""),
940 (Role::Agent, "\"ROLE_AGENT\""),
941 ];
942
943 for (role, expected_json) in cases {
944 let json = serde_json::to_string(&role).unwrap();
945 assert_eq!(json, expected_json);
946 let back: Role = serde_json::from_str(&json).unwrap();
947 assert_eq!(back, role);
948 }
949
950 let back: Role = serde_json::from_str("\"\"").unwrap();
951 assert_eq!(back, Role::Unspecified);
952 }
953
954 #[test]
955 fn test_task_state_all_variants_serde() {
956 let cases = [
957 (TaskState::Unspecified, "TASK_STATE_UNSPECIFIED"),
958 (TaskState::Submitted, "TASK_STATE_SUBMITTED"),
959 (TaskState::Working, "TASK_STATE_WORKING"),
960 (TaskState::Completed, "TASK_STATE_COMPLETED"),
961 (TaskState::Failed, "TASK_STATE_FAILED"),
962 (TaskState::Canceled, "TASK_STATE_CANCELED"),
963 (TaskState::InputRequired, "TASK_STATE_INPUT_REQUIRED"),
964 (TaskState::Rejected, "TASK_STATE_REJECTED"),
965 (TaskState::AuthRequired, "TASK_STATE_AUTH_REQUIRED"),
966 ];
967
968 for (state, expected_str) in cases {
969 let json = serde_json::to_string(&state).unwrap();
970 assert_eq!(json, format!("\"{}\"", expected_str));
971 let back: TaskState = serde_json::from_str(&json).unwrap();
972 assert_eq!(back, state);
973 }
974
975 let back: TaskState = serde_json::from_str("\"\"").unwrap();
976 assert_eq!(back, TaskState::Unspecified);
977 }
978
979 #[test]
980 fn test_part_all_content_variants_serde() {
981 let parts = [
982 Part::text("hello"),
983 Part::raw(vec![1, 2, 3]),
984 Part::url("https://example.com"),
985 Part::data(serde_json::json!({"value": 1})),
986 ];
987
988 for part in parts {
989 let json = serde_json::to_string(&part).unwrap();
990 let back: Part = serde_json::from_str(&json).unwrap();
991 match (&part.content, &back.content) {
992 (PartContent::Text(a), PartContent::Text(b)) => assert_eq!(a, b),
993 (PartContent::Raw(a), PartContent::Raw(b)) => assert_eq!(a, b),
994 (PartContent::Url(a), PartContent::Url(b)) => assert_eq!(a, b),
995 (PartContent::Data(_), PartContent::Data(_)) => {}
996 _ => panic!("mismatched part content variants"),
997 }
998 }
999 }
1000
1001 #[test]
1002 fn test_send_message_response_message_deserialize() {
1003 let json = serde_json::json!({
1004 "message": {
1005 "messageId": "m1",
1006 "role": "ROLE_AGENT",
1007 "parts": [{"text": "hello"}]
1008 }
1009 });
1010 let back: SendMessageResponse = serde_json::from_value(json).unwrap();
1011 assert!(matches!(back, SendMessageResponse::Message(_)));
1012 }
1013
1014 #[test]
1015 fn test_new_id_functions() {
1016 let task_id = new_task_id();
1017 assert!(!task_id.is_empty());
1018 let ctx_id = new_context_id();
1019 assert!(!ctx_id.is_empty());
1020 let msg_id = new_message_id();
1021 assert!(!msg_id.is_empty());
1022 let art_id = new_artifact_id();
1023 assert!(!art_id.is_empty());
1024 assert_ne!(task_id, ctx_id);
1026 }
1027
1028 #[test]
1029 fn test_role_default() {
1030 assert_eq!(Role::default(), Role::Unspecified);
1031 }
1032
1033 #[test]
1034 fn test_task_state_default() {
1035 assert_eq!(TaskState::default(), TaskState::Unspecified);
1036 }
1037
1038 #[test]
1039 fn test_list_tasks_request_serde() {
1040 let req = ListTasksRequest {
1041 context_id: Some("c1".to_string()),
1042 status: Some(TaskState::Working),
1043 page_size: Some(10),
1044 page_token: None,
1045 history_length: Some(5),
1046 status_timestamp_after: None,
1047 include_artifacts: Some(true),
1048 tenant: None,
1049 };
1050 let json = serde_json::to_string(&req).unwrap();
1051 let back: ListTasksRequest = serde_json::from_str(&json).unwrap();
1052 assert_eq!(req, back);
1053 }
1054
1055 #[test]
1056 fn test_cancel_task_request_serde() {
1057 let req = CancelTaskRequest {
1058 id: "t1".to_string(),
1059 metadata: None,
1060 tenant: Some("ten".to_string()),
1061 };
1062 let json = serde_json::to_string(&req).unwrap();
1063 let back: CancelTaskRequest = serde_json::from_str(&json).unwrap();
1064 assert_eq!(req, back);
1065 }
1066
1067 #[test]
1068 fn test_subscribe_to_task_request_serde() {
1069 let req = SubscribeToTaskRequest {
1070 id: "t1".to_string(),
1071 tenant: None,
1072 };
1073 let json = serde_json::to_string(&req).unwrap();
1074 let back: SubscribeToTaskRequest = serde_json::from_str(&json).unwrap();
1075 assert_eq!(req, back);
1076 }
1077}