33import static com .google .common .collect .ImmutableList .toImmutableList ;
44
55import com .google .adk .agents .InvocationContext ;
6- import com .google .adk .events .Event ;
76import com .google .common .collect .ImmutableList ;
87import com .google .genai .types .Content ;
9- import com .google .genai .types .Part ;
108import io .a2a .spec .Message ;
11- import io .a2a .spec .TextPart ;
12- import java .util .ArrayList ;
13- import java .util .List ;
9+ import io .a2a .spec .Part ;
10+ import java .util .Collection ;
1411import java .util .Optional ;
1512import java .util .UUID ;
1613import org .slf4j .Logger ;
@@ -28,47 +25,35 @@ public final class EventConverter {
2825 private EventConverter () {}
2926
3027 /**
31- * Aggregation mode for converting events to A2A messages .
28+ * Converts an ADK InvocationContext to an A2A Message .
3229 *
33- * <p>AS_IS: Parts are aggregated as-is.
30+ * <p>It combines all the events in the session, plus the user content, converted into A2A Parts,
31+ * into a single A2A Message.
3432 *
35- * <p>EXTERNAL_HANDOFF: Parts are aggregated as-is, except for function responses, which are
36- * converted to text parts with the function name and response map.
33+ * <p>If the context has no events, or no suitable content to build the message, an empty optional
34+ * is returned.
35+ *
36+ * @param context The ADK InvocationContext to convert.
37+ * @return The converted A2A Message.
3738 */
38- public enum AggregationMode {
39- AS_IS ,
40- EXTERNAL_HANDOFF
41- }
42-
43- public static ImmutableList <io .a2a .spec .Part <?>> contentToParts (Optional <Content > content ) {
44- if (content .isPresent () && content .get ().parts ().isPresent ()) {
45- return content .get ().parts ().get ().stream ()
46- .map (PartConverter ::fromGenaiPart )
47- .flatMap (Optional ::stream )
48- .collect (toImmutableList ());
49- }
50- return ImmutableList .of ();
51- }
52-
5339 public static Optional <Message > convertEventsToA2AMessage (InvocationContext context ) {
54- return convertEventsToA2AMessage (context , AggregationMode .AS_IS );
55- }
56-
57- public static Optional <Message > convertEventsToA2AMessage (
58- InvocationContext context , AggregationMode mode ) {
5940 if (context .session ().events ().isEmpty ()) {
6041 logger .warn ("No events in session, cannot convert to A2A message." );
6142 return Optional .empty ();
6243 }
6344
64- List <io .a2a .spec .Part <?>> parts = new ArrayList <>();
65- for (Event event : context .session ().events ()) {
66- appendContentParts (event .content (), mode , parts );
67- }
45+ ImmutableList .Builder <Part <?>> partsBuilder = ImmutableList .builder ();
6846
6947 context
70- .userContent ()
71- .ifPresent (content -> appendContentParts (Optional .of (content ), mode , parts ));
48+ .session ()
49+ .events ()
50+ .forEach (
51+ event ->
52+ partsBuilder .addAll (
53+ contentToParts (event .content (), event .partial ().orElse (false ))));
54+ partsBuilder .addAll (contentToParts (context .userContent (), false ));
55+
56+ ImmutableList <Part <?>> parts = partsBuilder .build ();
7257
7358 if (parts .isEmpty ()) {
7459 logger .warn ("No suitable content found to build A2A request message." );
@@ -83,37 +68,11 @@ public static Optional<Message> convertEventsToA2AMessage(
8368 .build ());
8469 }
8570
86- private static void appendContentParts (
87- Optional <Content > contentOpt , AggregationMode mode , List <io .a2a .spec .Part <?>> target ) {
88- if (contentOpt .isEmpty () || contentOpt .get ().parts ().isEmpty ()) {
89- return ;
90- }
91-
92- for (Part part : contentOpt .get ().parts ().get ()) {
93- if (part .text ().isPresent ()) {
94- target .add (new TextPart (part .text ().get ()));
95- continue ;
96- }
97-
98- if (part .functionCall ().isPresent ()) {
99- if (mode == AggregationMode .AS_IS ) {
100- PartConverter .convertGenaiPartToA2aPart (part ).ifPresent (target ::add );
101- }
102- continue ;
103- }
104-
105- if (part .functionResponse ().isPresent ()) {
106- if (mode == AggregationMode .AS_IS ) {
107- PartConverter .convertGenaiPartToA2aPart (part ).ifPresent (target ::add );
108- } else {
109- String name = part .functionResponse ().get ().name ().orElse ("" );
110- String mapStr = String .valueOf (part .functionResponse ().get ().response ().orElse (null ));
111- target .add (new TextPart (String .format ("%s response: %s" , name , mapStr )));
112- }
113- continue ;
114- }
115-
116- PartConverter .fromGenaiPart (part ).ifPresent (target ::add );
117- }
71+ public static ImmutableList <Part <?>> contentToParts (
72+ Optional <Content > content , boolean isPartial ) {
73+ return content .flatMap (Content ::parts ).stream ()
74+ .flatMap (Collection ::stream )
75+ .map (part -> PartConverter .fromGenaiPart (part , isPartial ))
76+ .collect (toImmutableList ());
11877 }
11978}
0 commit comments