Skip to content
Draft

WIP #453

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
17 changes: 15 additions & 2 deletions client/base/src/main/java/io/a2a/client/MessageEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ public MessageEvent(Message message) {
public Message getMessage() {
return message;
}
}


@Override
public String toString() {
String messageAsString = "{"
+ "role=" + message.getRole()
+ ", parts=" + message.getParts()
+ ", messageId=" + message.getMessageId()
+ ", contextId=" + message.getContextId()
+ ", taskId=" + message.getTaskId()
+ ", metadata=" + message.getMetadata()
+ ", kind=" + message.getKind()
+ ", referenceTaskIds=" + message.getReferenceTaskIds()
+ ", extensions=" + message.getExtensions() + '}';
return "MessageEvent{" + "message=" + messageAsString + '}';
}
}
2 changes: 1 addition & 1 deletion examples/helloworld/client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

<artifactId>a2a-java-sdk-examples-client</artifactId>

<name>Java SDK A2A Examples</name>
<name>Java SDK A2A HelloWorld Example - Client</name>
<description>Examples for the Java SDK for the Agent2Agent Protocol (A2A)</description>

<dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,18 @@
import io.a2a.A2A;

import io.a2a.client.Client;
import io.a2a.client.ClientBuilder;
import io.a2a.client.ClientEvent;
import io.a2a.client.MessageEvent;
import io.a2a.client.http.A2ACardResolver;
import io.a2a.client.transport.jsonrpc.JSONRPCTransport;
import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfig;
import io.a2a.client.transport.spi.interceptors.ClientCallContext;
import io.a2a.common.A2AHeaders;
import io.a2a.spec.AgentCard;
import io.a2a.spec.Message;
import io.a2a.spec.Part;
import io.a2a.spec.TextPart;
import java.util.Collections;

/**
* A simple example of using the A2A Java SDK to communicate with an A2A server.
Expand Down Expand Up @@ -61,6 +63,7 @@ public static void main(String[] args) {
List<BiConsumer<ClientEvent, AgentCard>> consumers = new ArrayList<>();
consumers.add((event, agentCard) -> {
if (event instanceof MessageEvent messageEvent) {
System.out.println("Received client MessageEvent: " + messageEvent);
Message responseMessage = messageEvent.getMessage();
StringBuilder textBuilder = new StringBuilder();
if (responseMessage.getParts() != null) {
Expand Down Expand Up @@ -89,11 +92,11 @@ public static void main(String[] args) {
.streamingErrorHandler(streamingErrorHandler)
.withTransport(JSONRPCTransport.class, new JSONRPCTransportConfig())
.build();
ClientCallContext clientContext = new ClientCallContext(Collections.emptyMap(), Map.of(A2AHeaders.X_A2A_EXTENSIONS, "https://github.com/a2aproject/a2a-samples/extensions/timestamp/v1"));

Message message = A2A.toUserMessage(MESSAGE_TEXT); // the message ID will be automatically generated for you

System.out.println("Sending message: " + MESSAGE_TEXT);
client.sendMessage(message);
client.sendMessage(message, clientContext);
System.out.println("Message sent successfully. Responses will be handled by the configured consumers.");

try {
Expand Down
7 changes: 6 additions & 1 deletion examples/helloworld/server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,19 @@

<artifactId>a2a-java-sdk-examples-server</artifactId>

<name>Java SDK A2A Examples</name>
<name>Java SDK A2A HelloWorld Example - Server</name>
<description>Examples for the Java SDK for the Agent2Agent Protocol (A2A)</description>

<dependencies>
<dependency>
<groupId>io.github.a2asdk</groupId>
<artifactId>a2a-java-sdk-client</artifactId>
</dependency>
<dependency>
<groupId>io.github.a2asdk</groupId>
<artifactId>a2a-java-timestamp-extension</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.github.a2asdk</groupId>
<artifactId>a2a-java-sdk-reference-jsonrpc</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import io.a2a.server.agentexecution.RequestContext;
import io.a2a.server.events.EventQueue;
import io.a2a.A2A;
import io.a2a.extension.timestamp.TimeStampAgentExecutorWrapper;
import io.a2a.spec.JSONRPCError;
import io.a2a.spec.UnsupportedOperationError;

Expand All @@ -15,7 +16,7 @@ public class AgentExecutorProducer {

@Produces
public AgentExecutor agentExecutor() {
return new AgentExecutor() {
return new TimeStampAgentExecutorWrapper(new AgentExecutor() {
@Override
public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError {
eventQueue.enqueueEvent(A2A.toAgentMessage("Hello World"));
Expand All @@ -25,6 +26,6 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP
public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError {
throw new UnsupportedOperationError();
}
};
});
}
}
40 changes: 40 additions & 0 deletions extensions/timestamp/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.github.a2asdk</groupId>
<artifactId>a2a-java-sdk-parent</artifactId>
<version>0.4.0.Alpha1-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

<artifactId>a2a-java-timestamp-extension</artifactId>
<name>A2A Java SDK :: Extension :: Timestamp</name>
<description>Simple Timestamp Extension</description>

<dependencies>
<dependency>
<groupId>io.github.a2asdk</groupId>
<artifactId>a2a-java-sdk-server-common</artifactId>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package io.a2a.extension.timestamp;

import io.a2a.server.agentexecution.AgentExecutor;
import io.a2a.server.agentexecution.RequestContext;
import io.a2a.server.events.EventQueue;
import io.a2a.spec.JSONRPCError;
import java.util.logging.Logger;

public class TimeStampAgentExecutorWrapper implements AgentExecutor {

public static final String CORE_PATH = "github.com/a2aproject/a2a-samples/extensions/timestamp/v1";
public static final String URI = "https://" + CORE_PATH;
public static final String TIMESTAMP_FIELD = CORE_PATH + "/timestamp";

private static final Logger logger = Logger.getLogger(TimeStampAgentExecutorWrapper.class.getName());
private final AgentExecutor delegate;

public TimeStampAgentExecutorWrapper(AgentExecutor delegate) {
this.delegate = delegate;
}

@Override
public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError {
if(isActivated(context)) {
delegate.execute(context, new TimeStampEventQueue(eventQueue));
} else {
delegate.execute(context, eventQueue);
}
}

@Override
public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError {
if(isActivated(context)) {
delegate.cancel(context, new TimeStampEventQueue(eventQueue));
} else {
delegate.cancel(context, eventQueue);
}
}

private boolean isActivated(final RequestContext context) {
if (context.getCallContext().isExtensionActivated(URI)) {
return true;
}
if (context.getCallContext().isExtensionRequested(URI)) {
logger.info("Activated extension: " + URI);
context.getCallContext().activateExtension(URI);
return true;
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package io.a2a.extension.timestamp;

import static io.a2a.extension.timestamp.TimeStampAgentExecutorWrapper.TIMESTAMP_FIELD;
import static io.a2a.extension.timestamp.TimeStampAgentExecutorWrapper.URI;

import io.a2a.server.events.EventQueue;
import io.a2a.spec.Artifact;
import io.a2a.spec.Event;
import io.a2a.spec.Message;
import io.a2a.spec.Task;
import io.a2a.spec.TaskArtifactUpdateEvent;
import io.a2a.spec.TaskStatusUpdateEvent;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TimeStampEventQueue extends EventQueue {

private final EventQueue delegate;

public TimeStampEventQueue(EventQueue delegate) {
this.delegate = delegate;
}

@Override
public void enqueueEvent(Event event) {
this.delegate.enqueueEvent(timestampEvent(event));
}

private Event timestampEvent(Event event) {
if (event instanceof Message message) {
return processMessage(message);
}
if (event instanceof TaskArtifactUpdateEvent taskArtifactUpdateEvent) {
return processTaskArtifactUpdateEvent(taskArtifactUpdateEvent);
}
if (event instanceof TaskStatusUpdateEvent taskStatusUpdateEvent) {
return processTaskStatusUpdateEvent(taskStatusUpdateEvent);
}
if (event instanceof Task task) {
return processTask(task);
}
return event;
}

private Message processMessage(Message message) {
Map<String, Object> metadata = message.getMetadata() == null ? new HashMap<>() : new HashMap<>(message.getMetadata());
if (!metadata.containsKey(TIMESTAMP_FIELD)) {
metadata.put(TIMESTAMP_FIELD, OffsetDateTime.now(ZoneOffset.UTC));
}
List<String> extensions = message.getExtensions() == null ? new ArrayList<>() : new ArrayList<>(message.getExtensions());
if (!extensions.contains(URI)) {
extensions.add(URI);
}
return new Message.Builder(message).metadata(metadata).extensions(extensions).build();
}

private Task processTask(Task task) {
Map<String, Object> metadata = task.getMetadata() == null ? new HashMap<>() : new HashMap<>(task.getMetadata());
if (!metadata.containsKey(TIMESTAMP_FIELD)) {
metadata.put(TIMESTAMP_FIELD, OffsetDateTime.now(ZoneOffset.UTC));
}
List<Artifact> artifacts = new ArrayList<>();
for (Artifact artifact : task.getArtifacts()) {
artifacts.add(processArtifact(artifact));
}
return new Task.Builder(task).artifacts(artifacts).metadata(metadata).build();
}

private TaskStatusUpdateEvent processTaskStatusUpdateEvent(TaskStatusUpdateEvent taskStatusUpdateEvent) {
Map<String, Object> metadata = taskStatusUpdateEvent.getMetadata() == null ? new HashMap<>() : new HashMap<>(taskStatusUpdateEvent.getMetadata());
if (!metadata.containsKey(TIMESTAMP_FIELD)) {
metadata.put(TIMESTAMP_FIELD, OffsetDateTime.now(ZoneOffset.UTC));
}
return new TaskStatusUpdateEvent.Builder(taskStatusUpdateEvent).metadata(metadata).build();
}

private TaskArtifactUpdateEvent processTaskArtifactUpdateEvent(TaskArtifactUpdateEvent taskArtifactUpdateEvent) {
Map<String, Object> metadata = taskArtifactUpdateEvent.getMetadata() == null ? new HashMap<>() : new HashMap<>(taskArtifactUpdateEvent.getMetadata());
if (!metadata.containsKey(TIMESTAMP_FIELD)) {
metadata.put(TIMESTAMP_FIELD, OffsetDateTime.now(ZoneOffset.UTC));
}
if (taskArtifactUpdateEvent.getArtifact() != null) {
return new TaskArtifactUpdateEvent.Builder(taskArtifactUpdateEvent).artifact(processArtifact(taskArtifactUpdateEvent.getArtifact())).metadata(metadata).build();
}
return new TaskArtifactUpdateEvent.Builder(taskArtifactUpdateEvent).metadata(metadata).build();
}

private Artifact processArtifact(Artifact artifact) {
Map<String, Object> metadata = artifact.metadata() == null ? new HashMap<>() : new HashMap<>(artifact.metadata());
if (!metadata.containsKey(TIMESTAMP_FIELD)) {
metadata.put(TIMESTAMP_FIELD, OffsetDateTime.now(ZoneOffset.UTC));
}
List<String> extensions = artifact.extensions() == null ? new ArrayList<>() : new ArrayList<>(artifact.extensions());
if (!extensions.contains(URI)) {
extensions.add(URI);
}
return new Artifact.Builder(artifact).metadata(metadata).extensions(extensions).build();
}

@Override
public void awaitQueuePollerStart() throws InterruptedException {
this.delegate.awaitQueuePollerStart();
}

@Override
public void close() {
this.delegate.close();
}

@Override
public void close(boolean immediate) {
this.delegate.close(immediate);
}

@Override
public void close(boolean immediate, boolean notifyParent) {
this.delegate.close(immediate, notifyParent);
}

@Override
public void signalQueuePollerStarted() {
this.delegate.signalQueuePollerStarted();
}

@Override
public EventQueue tap() {
return this;
}
}
Loading
Loading