Skip to content

Commit 8d59ff2

Browse files
google-genai-botcopybara-github
authored andcommitted
ADK changes
PiperOrigin-RevId: 932668999
1 parent 287987a commit 8d59ff2

2 files changed

Lines changed: 102 additions & 34 deletions

File tree

a2a/src/main/java/com/google/adk/a2a/converters/PartConverter.java

Lines changed: 71 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,11 @@ public static com.google.genai.types.Part toGenaiPart(io.a2a.spec.Part<?> a2aPar
8383
if (a2aPart instanceof TextPart textPart) {
8484
com.google.genai.types.Part.Builder partBuilder =
8585
com.google.genai.types.Part.builder().text(textPart.getText());
86-
if (textPart.getMetadata() != null
87-
&& Objects.equals(textPart.getMetadata().get("thought"), Boolean.TRUE)) {
88-
partBuilder.thought(true);
86+
if (textPart.getMetadata() != null) {
87+
partBuilder.partMetadata(textPart.getMetadata());
88+
if (Objects.equals(textPart.getMetadata().get("thought"), true)) {
89+
partBuilder.thought(true);
90+
}
8991
}
9092
return partBuilder.build();
9193
}
@@ -108,14 +110,19 @@ public static ImmutableList<com.google.genai.types.Part> toGenaiParts(
108110

109111
private static com.google.genai.types.Part convertFilePartToGenAiPart(FilePart filePart) {
110112
FileContent fileContent = filePart.getFile();
113+
Map<String, Object> metadata = filePart.getMetadata();
111114
if (fileContent instanceof FileWithUri fileWithUri) {
112-
return com.google.genai.types.Part.builder()
113-
.fileData(
114-
FileData.builder()
115-
.fileUri(fileWithUri.uri())
116-
.mimeType(fileWithUri.mimeType())
117-
.build())
118-
.build();
115+
com.google.genai.types.Part.Builder builder =
116+
com.google.genai.types.Part.builder()
117+
.fileData(
118+
FileData.builder()
119+
.fileUri(fileWithUri.uri())
120+
.mimeType(fileWithUri.mimeType())
121+
.build());
122+
if (metadata != null) {
123+
builder.partMetadata(metadata);
124+
}
125+
return builder.build();
119126
}
120127

121128
if (fileContent instanceof FileWithBytes fileWithBytes) {
@@ -124,9 +131,13 @@ private static com.google.genai.types.Part convertFilePartToGenAiPart(FilePart f
124131
throw new GenAiFieldMissingException("FileWithBytes missing byte content");
125132
}
126133
byte[] decoded = Base64.getDecoder().decode(bytesString);
127-
return com.google.genai.types.Part.builder()
128-
.inlineData(Blob.builder().data(decoded).mimeType(fileWithBytes.mimeType()).build())
129-
.build();
134+
com.google.genai.types.Part.Builder builder =
135+
com.google.genai.types.Part.builder()
136+
.inlineData(Blob.builder().data(decoded).mimeType(fileWithBytes.mimeType()).build());
137+
if (metadata != null) {
138+
builder.partMetadata(metadata);
139+
}
140+
return builder.build();
130141
}
131142

132143
throw new IllegalArgumentException("Unsupported FilePart content: " + fileContent.getClass());
@@ -145,24 +156,33 @@ private static com.google.genai.types.Part convertDataPartToGenAiPart(DataPart d
145156
String functionName = String.valueOf(data.getOrDefault(NAME_KEY, ""));
146157
String functionId = String.valueOf(data.getOrDefault(ID_KEY, ""));
147158
Map<String, Object> args = coerceToMap(data.get(ARGS_KEY));
148-
return com.google.genai.types.Part.builder()
149-
.functionCall(FunctionCall.builder().name(functionName).id(functionId).args(args).build())
150-
.build();
159+
com.google.genai.types.Part.Builder builder =
160+
com.google.genai.types.Part.builder()
161+
.functionCall(
162+
FunctionCall.builder().name(functionName).id(functionId).args(args).build());
163+
if (!metadata.isEmpty()) {
164+
builder.partMetadata(metadata);
165+
}
166+
return builder.build();
151167
}
152168

153169
if ((data.containsKey(NAME_KEY) && data.containsKey(RESPONSE_KEY))
154170
|| metadataType.equals(A2ADataPartMetadataType.FUNCTION_RESPONSE.getType())) {
155171
String functionName = String.valueOf(data.getOrDefault(NAME_KEY, ""));
156172
String functionId = String.valueOf(data.getOrDefault(ID_KEY, ""));
157173
Map<String, Object> response = coerceToMap(data.get(RESPONSE_KEY));
158-
return com.google.genai.types.Part.builder()
159-
.functionResponse(
160-
FunctionResponse.builder()
161-
.name(functionName)
162-
.id(functionId)
163-
.response(response)
164-
.build())
165-
.build();
174+
com.google.genai.types.Part.Builder builder =
175+
com.google.genai.types.Part.builder()
176+
.functionResponse(
177+
FunctionResponse.builder()
178+
.name(functionName)
179+
.id(functionId)
180+
.response(response)
181+
.build());
182+
if (!metadata.isEmpty()) {
183+
builder.partMetadata(metadata);
184+
}
185+
return builder.build();
166186
}
167187

168188
if ((data.containsKey(CODE_KEY) && data.containsKey(LANGUAGE_KEY))
@@ -171,26 +191,42 @@ private static com.google.genai.types.Part convertDataPartToGenAiPart(DataPart d
171191
String language =
172192
String.valueOf(
173193
data.getOrDefault(LANGUAGE_KEY, Language.Known.LANGUAGE_UNSPECIFIED.toString()));
174-
return com.google.genai.types.Part.builder()
175-
.executableCode(
176-
ExecutableCode.builder().code(code).language(new Language(language)).build())
177-
.build();
194+
com.google.genai.types.Part.Builder builder =
195+
com.google.genai.types.Part.builder()
196+
.executableCode(
197+
ExecutableCode.builder().code(code).language(new Language(language)).build());
198+
if (!metadata.isEmpty()) {
199+
builder.partMetadata(metadata);
200+
}
201+
return builder.build();
178202
}
179203

180204
if ((data.containsKey(OUTCOME_KEY) && data.containsKey(OUTPUT_KEY))
181205
|| metadataType.equals(A2ADataPartMetadataType.CODE_EXECUTION_RESULT.getType())) {
182206
String outcome =
183207
String.valueOf(data.getOrDefault(OUTCOME_KEY, Outcome.Known.OUTCOME_OK).toString());
184208
String output = String.valueOf(data.getOrDefault(OUTPUT_KEY, ""));
185-
return com.google.genai.types.Part.builder()
186-
.codeExecutionResult(
187-
CodeExecutionResult.builder().outcome(new Outcome(outcome)).output(output).build())
188-
.build();
209+
com.google.genai.types.Part.Builder builder =
210+
com.google.genai.types.Part.builder()
211+
.codeExecutionResult(
212+
CodeExecutionResult.builder()
213+
.outcome(new Outcome(outcome))
214+
.output(output)
215+
.build());
216+
if (!metadata.isEmpty()) {
217+
builder.partMetadata(metadata);
218+
}
219+
return builder.build();
189220
}
190221

191222
try {
192223
String json = objectMapper.writeValueAsString(data);
193-
return com.google.genai.types.Part.builder().text(json).build();
224+
com.google.genai.types.Part.Builder builder =
225+
com.google.genai.types.Part.builder().text(json);
226+
if (!metadata.isEmpty()) {
227+
builder.partMetadata(metadata);
228+
}
229+
return builder.build();
194230
} catch (JsonProcessingException e) {
195231
throw new IllegalArgumentException("Failed to serialize DataPart payload", e);
196232
}
@@ -309,10 +345,11 @@ public static io.a2a.spec.Part<?> fromGenaiPart(Part part, boolean isPartial) {
309345
if (isPartial) {
310346
metadata.put(A2AMetadataKey.PARTIAL.getType(), true);
311347
}
348+
part.partMetadata().ifPresent(metadata::putAll);
312349

313350
if (part.text().isPresent()) {
314351
addValueIfPresent(metadata, "thought", part.thought());
315-
return new TextPart(part.text().get(), metadata.buildOrThrow());
352+
return new TextPart(part.text().get(), metadata.buildKeepingLast());
316353
}
317354

318355
if (part.fileData().isPresent() || part.inlineData().isPresent()) {

a2a/src/test/java/com/google/adk/a2a/converters/PartConverterTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public void toGenaiPart_withTextPartMetadataWithoutThought_returnsGenaiTextPartW
6161

6262
assertThat(result.text()).hasValue("Thinking process");
6363
assertThat(result.thought()).isEmpty();
64+
assertThat(result.partMetadata()).hasValue(ImmutableMap.of("otherKey", "value"));
6465
}
6566

6667
@Test
@@ -374,4 +375,34 @@ public void toGenaiPart_dataPartWithNonMapCoercedToMap() {
374375
assertThat(result.functionCall()).isPresent();
375376
assertThat(result.functionCall().get().args()).hasValue(ImmutableMap.of("value", 123));
376377
}
378+
379+
@Test
380+
public void toGenaiPart_withTextPartMetadata_propagatesMetadata() {
381+
TextPart textPart = new TextPart("Hello", ImmutableMap.of("key", "value"));
382+
383+
Part result = PartConverter.toGenaiPart(textPart);
384+
385+
assertThat(result.partMetadata()).hasValue(ImmutableMap.of("key", "value"));
386+
}
387+
388+
@Test
389+
public void toGenaiPart_withFilePartMetadata_propagatesMetadata() {
390+
FilePart filePart =
391+
new FilePart(
392+
new FileWithUri("text/plain", "file.txt", "http://file.txt"),
393+
ImmutableMap.of("key", "value"));
394+
395+
Part result = PartConverter.toGenaiPart(filePart);
396+
397+
assertThat(result.partMetadata()).hasValue(ImmutableMap.of("key", "value"));
398+
}
399+
400+
@Test
401+
public void fromGenaiPart_withPartMetadata_propagatesMetadata() {
402+
Part part = Part.builder().text("Hello").partMetadata(ImmutableMap.of("key", "value")).build();
403+
404+
io.a2a.spec.Part<?> result = PartConverter.fromGenaiPart(part, false);
405+
406+
assertThat(result.getMetadata()).containsExactly("key", "value");
407+
}
377408
}

0 commit comments

Comments
 (0)