Skip to main content

radkit/models/
utils.rs

1//! Utility functions for working with models and A2A protocol types.
2
3use a2a_types::{Artifact as A2AArtifact, Message as A2AMessage, Part as A2APart, Role as A2ARole};
4use uuid::Uuid;
5
6use crate::agent::Artifact;
7use crate::models::{Content, ContentPart, Role};
8
9/// Creates an A2A `Message` from radkit `Content`.
10pub fn create_a2a_message(
11    context_id: Option<&str>,
12    task_id: Option<&str>,
13    role: Role,
14    content: Content,
15) -> A2AMessage {
16    let parts: Vec<A2APart> = content
17        .into_parts()
18        .into_iter()
19        .filter_map(ContentPart::into_a2a_part)
20        .collect();
21
22    let proto_role = match role {
23        Role::User => A2ARole::User,
24        Role::Assistant | Role::System | Role::Tool => A2ARole::Agent,
25    };
26
27    A2AMessage {
28        message_id: Uuid::new_v4().to_string(),
29        role: proto_role as i32,
30        context_id: context_id.unwrap_or_default().to_string(),
31        task_id: task_id.unwrap_or_default().to_string(),
32        parts,
33        reference_task_ids: Vec::new(),
34        extensions: Vec::new(),
35        metadata: None,
36    }
37}
38
39/// Converts a radkit `Artifact` to `a2a_types::Artifact`.
40pub fn artifact_to_a2a(artifact: &Artifact) -> A2AArtifact {
41    let parts: Vec<A2APart> = artifact
42        .content()
43        .clone()
44        .into_parts()
45        .into_iter()
46        .filter_map(ContentPart::into_a2a_part)
47        .collect();
48
49    A2AArtifact {
50        artifact_id: artifact.name().to_string(),
51        parts,
52        name: artifact.name().to_string(),
53        description: String::new(),
54        extensions: Vec::new(),
55        metadata: None,
56    }
57}
58
59/// Converts a slice of radkit `Artifact`s to `a2a_types::Artifact`s.
60pub fn artifacts_to_a2a(artifacts: &[Artifact]) -> Vec<A2AArtifact> {
61    artifacts.iter().map(artifact_to_a2a).collect()
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67    use crate::agent::Artifact;
68
69    #[test]
70    fn create_a2a_message_maps_roles_and_parts() {
71        let content = Content::from_text("Hello");
72        let message =
73            create_a2a_message(Some("ctx-1"), Some("task-2"), Role::User, content.clone());
74
75        assert_eq!(message.context_id, "ctx-1");
76        assert_eq!(message.task_id, "task-2");
77        assert_eq!(message.role, A2ARole::User as i32);
78        assert_eq!(message.parts.len(), content.parts().len());
79    }
80
81    #[test]
82    fn artifact_conversion_preserves_name_and_parts() {
83        let artifact = Artifact::from_text("notes.txt", "example");
84        let converted = artifact_to_a2a(&artifact);
85        assert_eq!(converted.artifact_id, "notes.txt");
86        assert_eq!(converted.name, "notes.txt");
87        assert_eq!(converted.parts.len(), artifact.content().parts().len());
88
89        let multiple = artifacts_to_a2a(&[artifact.clone(), artifact]);
90        assert_eq!(multiple.len(), 2);
91    }
92}