Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.google.adk.a2a.common;

/** Exception thrown when the the genai class has an empty field. */
public class GenAiFieldMissingException extends RuntimeException {
public GenAiFieldMissingException(String message) {
super(message);
}

public GenAiFieldMissingException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.google.adk.a2a.converters;

/** Enum for the type of A2A DataPart metadata. */
public enum A2ADataPartMetadataType {
FUNCTION_RESPONSE("function_response"),
FUNCTION_CALL("function_call"),
CODE_EXECUTION_RESULT("code_execution_result"),
EXECUTABLE_CODE("executable_code");

private final String type;

private A2ADataPartMetadataType(String type) {
this.type = type;
}

public String getType() {
return type;
}
}

This file was deleted.

93 changes: 26 additions & 67 deletions a2a/src/main/java/com/google/adk/a2a/converters/EventConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,11 @@
import static com.google.common.collect.ImmutableList.toImmutableList;

import com.google.adk.agents.InvocationContext;
import com.google.adk.events.Event;
import com.google.common.collect.ImmutableList;
import com.google.genai.types.Content;
import com.google.genai.types.Part;
import io.a2a.spec.Message;
import io.a2a.spec.TextPart;
import java.util.ArrayList;
import java.util.List;
import io.a2a.spec.Part;
import java.util.Collection;
import java.util.Optional;
import java.util.UUID;
import org.slf4j.Logger;
Expand All @@ -28,47 +25,35 @@ public final class EventConverter {
private EventConverter() {}

/**
* Aggregation mode for converting events to A2A messages.
* Converts an ADK InvocationContext to an A2A Message.
*
* <p>AS_IS: Parts are aggregated as-is.
* <p>It combines all the events in the session, plus the user content, converted into A2A Parts,
* into a single A2A Message.
*
* <p>EXTERNAL_HANDOFF: Parts are aggregated as-is, except for function responses, which are
* converted to text parts with the function name and response map.
* <p>If the context has no events, or no suitable content to build the message, an empty optional
* is returned.
*
* @param context The ADK InvocationContext to convert.
* @return The converted A2A Message.
*/
public enum AggregationMode {
AS_IS,
EXTERNAL_HANDOFF
}

public static ImmutableList<io.a2a.spec.Part<?>> contentToParts(Optional<Content> content) {
if (content.isPresent() && content.get().parts().isPresent()) {
return content.get().parts().get().stream()
.map(PartConverter::fromGenaiPart)
.flatMap(Optional::stream)
.collect(toImmutableList());
}
return ImmutableList.of();
}

public static Optional<Message> convertEventsToA2AMessage(InvocationContext context) {
return convertEventsToA2AMessage(context, AggregationMode.AS_IS);
}

public static Optional<Message> convertEventsToA2AMessage(
InvocationContext context, AggregationMode mode) {
if (context.session().events().isEmpty()) {
logger.warn("No events in session, cannot convert to A2A message.");
return Optional.empty();
}

List<io.a2a.spec.Part<?>> parts = new ArrayList<>();
for (Event event : context.session().events()) {
appendContentParts(event.content(), mode, parts);
}
ImmutableList.Builder<Part<?>> partsBuilder = ImmutableList.builder();

context
.userContent()
.ifPresent(content -> appendContentParts(Optional.of(content), mode, parts));
.session()
.events()
.forEach(
event ->
partsBuilder.addAll(
contentToParts(event.content(), event.partial().orElse(false))));
partsBuilder.addAll(contentToParts(context.userContent(), false));

ImmutableList<Part<?>> parts = partsBuilder.build();

if (parts.isEmpty()) {
logger.warn("No suitable content found to build A2A request message.");
Expand All @@ -83,37 +68,11 @@ public static Optional<Message> convertEventsToA2AMessage(
.build());
}

private static void appendContentParts(
Optional<Content> contentOpt, AggregationMode mode, List<io.a2a.spec.Part<?>> target) {
if (contentOpt.isEmpty() || contentOpt.get().parts().isEmpty()) {
return;
}

for (Part part : contentOpt.get().parts().get()) {
if (part.text().isPresent()) {
target.add(new TextPart(part.text().get()));
continue;
}

if (part.functionCall().isPresent()) {
if (mode == AggregationMode.AS_IS) {
PartConverter.convertGenaiPartToA2aPart(part).ifPresent(target::add);
}
continue;
}

if (part.functionResponse().isPresent()) {
if (mode == AggregationMode.AS_IS) {
PartConverter.convertGenaiPartToA2aPart(part).ifPresent(target::add);
} else {
String name = part.functionResponse().get().name().orElse("");
String mapStr = String.valueOf(part.functionResponse().get().response().orElse(null));
target.add(new TextPart(String.format("%s response: %s", name, mapStr)));
}
continue;
}

PartConverter.fromGenaiPart(part).ifPresent(target::add);
}
public static ImmutableList<Part<?>> contentToParts(
Optional<Content> content, boolean isPartial) {
return content.flatMap(Content::parts).stream()
.flatMap(Collection::stream)
.map(part -> PartConverter.fromGenaiPart(part, isPartial))
.collect(toImmutableList());
}
}
Loading
Loading