From e69adb39bef5cd3188b3adc12593bd9253ec33e4 Mon Sep 17 00:00:00 2001 From: Google Team Member Date: Tue, 16 Jun 2026 09:06:59 -0700 Subject: [PATCH] ADK changes PiperOrigin-RevId: 933120582 --- .../adk/a2a/converters/PartConverter.java | 105 ++++++++++++------ .../adk/a2a/converters/PartConverterTest.java | 31 ++++++ 2 files changed, 102 insertions(+), 34 deletions(-) diff --git a/a2a/src/main/java/com/google/adk/a2a/converters/PartConverter.java b/a2a/src/main/java/com/google/adk/a2a/converters/PartConverter.java index 2e957695b..eb1d2f112 100644 --- a/a2a/src/main/java/com/google/adk/a2a/converters/PartConverter.java +++ b/a2a/src/main/java/com/google/adk/a2a/converters/PartConverter.java @@ -83,9 +83,11 @@ public static com.google.genai.types.Part toGenaiPart(io.a2a.spec.Part a2aPar if (a2aPart instanceof TextPart textPart) { com.google.genai.types.Part.Builder partBuilder = com.google.genai.types.Part.builder().text(textPart.getText()); - if (textPart.getMetadata() != null - && Objects.equals(textPart.getMetadata().get("thought"), Boolean.TRUE)) { - partBuilder.thought(true); + if (textPart.getMetadata() != null) { + partBuilder.partMetadata(textPart.getMetadata()); + if (Objects.equals(textPart.getMetadata().get("thought"), true)) { + partBuilder.thought(true); + } } return partBuilder.build(); } @@ -108,14 +110,19 @@ public static ImmutableList toGenaiParts( private static com.google.genai.types.Part convertFilePartToGenAiPart(FilePart filePart) { FileContent fileContent = filePart.getFile(); + Map metadata = filePart.getMetadata(); if (fileContent instanceof FileWithUri fileWithUri) { - return com.google.genai.types.Part.builder() - .fileData( - FileData.builder() - .fileUri(fileWithUri.uri()) - .mimeType(fileWithUri.mimeType()) - .build()) - .build(); + com.google.genai.types.Part.Builder builder = + com.google.genai.types.Part.builder() + .fileData( + FileData.builder() + .fileUri(fileWithUri.uri()) + .mimeType(fileWithUri.mimeType()) + .build()); + if (metadata != null) { + builder.partMetadata(metadata); + } + return builder.build(); } if (fileContent instanceof FileWithBytes fileWithBytes) { @@ -124,9 +131,13 @@ private static com.google.genai.types.Part convertFilePartToGenAiPart(FilePart f throw new GenAiFieldMissingException("FileWithBytes missing byte content"); } byte[] decoded = Base64.getDecoder().decode(bytesString); - return com.google.genai.types.Part.builder() - .inlineData(Blob.builder().data(decoded).mimeType(fileWithBytes.mimeType()).build()) - .build(); + com.google.genai.types.Part.Builder builder = + com.google.genai.types.Part.builder() + .inlineData(Blob.builder().data(decoded).mimeType(fileWithBytes.mimeType()).build()); + if (metadata != null) { + builder.partMetadata(metadata); + } + return builder.build(); } throw new IllegalArgumentException("Unsupported FilePart content: " + fileContent.getClass()); @@ -145,9 +156,14 @@ private static com.google.genai.types.Part convertDataPartToGenAiPart(DataPart d String functionName = String.valueOf(data.getOrDefault(NAME_KEY, "")); String functionId = String.valueOf(data.getOrDefault(ID_KEY, "")); Map args = coerceToMap(data.get(ARGS_KEY)); - return com.google.genai.types.Part.builder() - .functionCall(FunctionCall.builder().name(functionName).id(functionId).args(args).build()) - .build(); + com.google.genai.types.Part.Builder builder = + com.google.genai.types.Part.builder() + .functionCall( + FunctionCall.builder().name(functionName).id(functionId).args(args).build()); + if (!metadata.isEmpty()) { + builder.partMetadata(metadata); + } + return builder.build(); } if ((data.containsKey(NAME_KEY) && data.containsKey(RESPONSE_KEY)) @@ -155,14 +171,18 @@ private static com.google.genai.types.Part convertDataPartToGenAiPart(DataPart d String functionName = String.valueOf(data.getOrDefault(NAME_KEY, "")); String functionId = String.valueOf(data.getOrDefault(ID_KEY, "")); Map response = coerceToMap(data.get(RESPONSE_KEY)); - return com.google.genai.types.Part.builder() - .functionResponse( - FunctionResponse.builder() - .name(functionName) - .id(functionId) - .response(response) - .build()) - .build(); + com.google.genai.types.Part.Builder builder = + com.google.genai.types.Part.builder() + .functionResponse( + FunctionResponse.builder() + .name(functionName) + .id(functionId) + .response(response) + .build()); + if (!metadata.isEmpty()) { + builder.partMetadata(metadata); + } + return builder.build(); } if ((data.containsKey(CODE_KEY) && data.containsKey(LANGUAGE_KEY)) @@ -171,10 +191,14 @@ private static com.google.genai.types.Part convertDataPartToGenAiPart(DataPart d String language = String.valueOf( data.getOrDefault(LANGUAGE_KEY, Language.Known.LANGUAGE_UNSPECIFIED.toString())); - return com.google.genai.types.Part.builder() - .executableCode( - ExecutableCode.builder().code(code).language(new Language(language)).build()) - .build(); + com.google.genai.types.Part.Builder builder = + com.google.genai.types.Part.builder() + .executableCode( + ExecutableCode.builder().code(code).language(new Language(language)).build()); + if (!metadata.isEmpty()) { + builder.partMetadata(metadata); + } + return builder.build(); } if ((data.containsKey(OUTCOME_KEY) && data.containsKey(OUTPUT_KEY)) @@ -182,15 +206,27 @@ private static com.google.genai.types.Part convertDataPartToGenAiPart(DataPart d String outcome = String.valueOf(data.getOrDefault(OUTCOME_KEY, Outcome.Known.OUTCOME_OK).toString()); String output = String.valueOf(data.getOrDefault(OUTPUT_KEY, "")); - return com.google.genai.types.Part.builder() - .codeExecutionResult( - CodeExecutionResult.builder().outcome(new Outcome(outcome)).output(output).build()) - .build(); + com.google.genai.types.Part.Builder builder = + com.google.genai.types.Part.builder() + .codeExecutionResult( + CodeExecutionResult.builder() + .outcome(new Outcome(outcome)) + .output(output) + .build()); + if (!metadata.isEmpty()) { + builder.partMetadata(metadata); + } + return builder.build(); } try { String json = objectMapper.writeValueAsString(data); - return com.google.genai.types.Part.builder().text(json).build(); + com.google.genai.types.Part.Builder builder = + com.google.genai.types.Part.builder().text(json); + if (!metadata.isEmpty()) { + builder.partMetadata(metadata); + } + return builder.build(); } catch (JsonProcessingException e) { throw new IllegalArgumentException("Failed to serialize DataPart payload", e); } @@ -309,10 +345,11 @@ public static io.a2a.spec.Part fromGenaiPart(Part part, boolean isPartial) { if (isPartial) { metadata.put(A2AMetadataKey.PARTIAL.getType(), true); } + part.partMetadata().ifPresent(metadata::putAll); if (part.text().isPresent()) { addValueIfPresent(metadata, "thought", part.thought()); - return new TextPart(part.text().get(), metadata.buildOrThrow()); + return new TextPart(part.text().get(), metadata.buildKeepingLast()); } if (part.fileData().isPresent() || part.inlineData().isPresent()) { diff --git a/a2a/src/test/java/com/google/adk/a2a/converters/PartConverterTest.java b/a2a/src/test/java/com/google/adk/a2a/converters/PartConverterTest.java index 563a9afdc..96f6add1c 100644 --- a/a2a/src/test/java/com/google/adk/a2a/converters/PartConverterTest.java +++ b/a2a/src/test/java/com/google/adk/a2a/converters/PartConverterTest.java @@ -61,6 +61,7 @@ public void toGenaiPart_withTextPartMetadataWithoutThought_returnsGenaiTextPartW assertThat(result.text()).hasValue("Thinking process"); assertThat(result.thought()).isEmpty(); + assertThat(result.partMetadata()).hasValue(ImmutableMap.of("otherKey", "value")); } @Test @@ -374,4 +375,34 @@ public void toGenaiPart_dataPartWithNonMapCoercedToMap() { assertThat(result.functionCall()).isPresent(); assertThat(result.functionCall().get().args()).hasValue(ImmutableMap.of("value", 123)); } + + @Test + public void toGenaiPart_withTextPartMetadata_propagatesMetadata() { + TextPart textPart = new TextPart("Hello", ImmutableMap.of("key", "value")); + + Part result = PartConverter.toGenaiPart(textPart); + + assertThat(result.partMetadata()).hasValue(ImmutableMap.of("key", "value")); + } + + @Test + public void toGenaiPart_withFilePartMetadata_propagatesMetadata() { + FilePart filePart = + new FilePart( + new FileWithUri("text/plain", "file.txt", "http://file.txt"), + ImmutableMap.of("key", "value")); + + Part result = PartConverter.toGenaiPart(filePart); + + assertThat(result.partMetadata()).hasValue(ImmutableMap.of("key", "value")); + } + + @Test + public void fromGenaiPart_withPartMetadata_propagatesMetadata() { + Part part = Part.builder().text("Hello").partMetadata(ImmutableMap.of("key", "value")).build(); + + io.a2a.spec.Part result = PartConverter.fromGenaiPart(part, false); + + assertThat(result.getMetadata()).containsExactly("key", "value"); + } }