3939import io .a2a .spec .FileWithUri ;
4040import io .a2a .spec .Message ;
4141import io .a2a .spec .TextPart ;
42+ import java .nio .charset .StandardCharsets ;
4243import java .util .Base64 ;
4344import java .util .HashMap ;
4445import java .util .List ;
@@ -66,6 +67,9 @@ public final class PartConverter {
6667 public static final String PARTIAL_ARGS_KEY = "partialArgs" ;
6768 public static final String SCHEDULING_KEY = "scheduling" ;
6869 public static final String PARTS_KEY = "parts" ;
70+ public static final String A2A_DATA_PART_START_TAG = "<a2a_datapart_json>" ;
71+ public static final String A2A_DATA_PART_END_TAG = "</a2a_datapart_json>" ;
72+ public static final String A2A_DATA_PART_TEXT_MIME_TYPE = "text/plain" ;
6973
7074 public static Optional <TextPart > toTextPart (io .a2a .spec .Part <?> part ) {
7175 if (part instanceof TextPart textPart ) {
@@ -190,7 +194,11 @@ private static com.google.genai.types.Part convertDataPartToGenAiPart(DataPart d
190194
191195 try {
192196 String json = objectMapper .writeValueAsString (data );
193- return com .google .genai .types .Part .builder ().text (json ).build ();
197+ String wrappedJson = A2A_DATA_PART_START_TAG + json + A2A_DATA_PART_END_TAG ;
198+ byte [] bytes = wrappedJson .getBytes (StandardCharsets .UTF_8 );
199+ return com .google .genai .types .Part .builder ()
200+ .inlineData (Blob .builder ().data (bytes ).mimeType (A2A_DATA_PART_TEXT_MIME_TYPE ).build ())
201+ .build ();
194202 } catch (JsonProcessingException e ) {
195203 throw new IllegalArgumentException ("Failed to serialize DataPart payload" , e );
196204 }
@@ -298,6 +306,37 @@ private static DataPart createDataPartFromExecutableCode(
298306 return new DataPart (data .buildOrThrow (), metadata .buildOrThrow ());
299307 }
300308
309+ private static boolean isDataPartInlineData (Blob blob ) {
310+ if (!blob .mimeType ().orElse ("" ).equals (A2A_DATA_PART_TEXT_MIME_TYPE )) {
311+ return false ;
312+ }
313+ byte [] data = blob .data ().orElse (null );
314+ if (data == null ) {
315+ return false ;
316+ }
317+ String str = new String (data , StandardCharsets .UTF_8 );
318+ return str .startsWith (A2A_DATA_PART_START_TAG ) && str .endsWith (A2A_DATA_PART_END_TAG );
319+ }
320+
321+ @ SuppressWarnings ("unchecked" )
322+ private static DataPart inlineDataToDataPart (
323+ Blob blob , ImmutableMap .Builder <String , Object > metadata ) {
324+ byte [] data = blob .data ().orElse (null );
325+ if (data == null ) {
326+ throw new IllegalArgumentException ("Blob data cannot be null" );
327+ }
328+ String str = new String (data , StandardCharsets .UTF_8 );
329+ String jsonContent =
330+ str .substring (
331+ A2A_DATA_PART_START_TAG .length (), str .length () - A2A_DATA_PART_END_TAG .length ());
332+ try {
333+ Map <String , Object > dataMap = objectMapper .readValue (jsonContent , Map .class );
334+ return new DataPart (dataMap , metadata .buildOrThrow ());
335+ } catch (Exception e ) {
336+ throw new IllegalArgumentException ("Failed to parse DataPart payload from inlineData" , e );
337+ }
338+ }
339+
301340 private PartConverter () {}
302341
303342 /** Convert a GenAI part into the A2A JSON representation. */
@@ -315,6 +354,10 @@ public static io.a2a.spec.Part<?> fromGenaiPart(Part part, boolean isPartial) {
315354 return new TextPart (part .text ().get (), metadata .buildOrThrow ());
316355 }
317356
357+ if (part .inlineData ().isPresent () && isDataPartInlineData (part .inlineData ().get ())) {
358+ return inlineDataToDataPart (part .inlineData ().get (), metadata );
359+ }
360+
318361 if (part .fileData ().isPresent () || part .inlineData ().isPresent ()) {
319362 return filePartToA2A (part , metadata );
320363 }
0 commit comments